Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

29360 lines (24750 sloc) 902.061 kB
/* Adventure Creator v2 Run-time engine
Started 27-May-99 (c) 1999-2011 Chris Jones
Adventure Game Studio source code Copyright 1999-2011 Chris Jones.
All rights reserved.
The AGS Editor Source Code is provided under the Artistic License 2.0
http://www.opensource.org/licenses/artistic-license-2.0.php
You MAY NOT compile your own builds of the engine without making it EXPLICITLY
CLEAR that the code has been altered from the Standard Version.
*/
#if !defined(IOS_VERSION) && !defined(PSP_VERSION) && !defined(ANDROID_VERSION)
int psp_video_framedrop = 1;
int psp_audio_enabled = 1;
int psp_midi_enabled = 1;
int psp_ignore_acsetup_cfg_file = 0;
int psp_clear_cache_on_room_change = 0;
volatile int psp_audio_multithreaded = 1;
int psp_midi_preload_patches = 0;
int psp_audio_cachesize = 10;
char psp_game_file_name[] = "ac2game.dat";
int psp_gfx_smooth_sprites = 1;
char psp_translation[] = "default";
#endif
#if (defined(MAC_VERSION) && !defined(IOS_VERSION)) || (defined(LINUX_VERSION) && !defined(PSP_VERSION) && !defined(ANDROID_VERSION))
#include <dlfcn.h>
#endif
#if defined(PSP_VERSION)
// PSP header.
#include <pspsdk.h>
#include <pspdebug.h>
#include <pspthreadman.h>
#include <psputils.h>
#include <pspmath.h>
#define cos(a) vfpu_cosf(a)
#define sin(a) vfpu_sinf(a)
#define tan(a) vfpu_tanf(a)
#define cos(a) vfpu_acosf(a)
#define sin(a) vfpu_asinf(a)
#define atan(a) vfpu_atanf(a)
#define atan2(a,b) vfpu_atan2f(a,b)
#define log(a) vfpu_logf(a)
#define exp(a) vfpu_expf(a)
#define cosh(a) vfpu_coshf(a)
#define sinh(a) vfpu_sinhf(a)
#define tanh(a) vfpu_tanhf(a)
#endif
#if defined(MAC_VERSION) || (defined(LINUX_VERSION) && !defined(PSP_VERSION))
#include <pthread.h>
pthread_t soundthread;
#endif
#if defined(ANDROID_VERSION)
#include <sys/stat.h>
#include <android/log.h>
extern "C" void android_render();
extern "C" void selectLatestSavegame();
extern bool psp_load_latest_savegame;
#endif
#if defined(IOS_VERSION)
extern "C" void ios_render();
#endif
// PSP specific variables:
extern int psp_video_framedrop; // Drop video frames if lagging behind audio?
extern int psp_audio_enabled; // Audio can be disabled in the config file.
extern int psp_midi_enabled; // Enable midi playback.
extern int psp_ignore_acsetup_cfg_file; // If set, the standard AGS config file is not being read.
extern int psp_clear_cache_on_room_change; // Clear the sprite cache on every room change.
extern void clear_sound_cache(); // Sound cache initialization.
extern char psp_game_file_name[]; // Game filename from the menu.
extern int psp_gfx_renderer; // Which renderer to use.
extern int psp_gfx_smooth_sprites; // usetup.enable_antialiasing
extern char psp_translation[]; // Translation file
int psp_is_old_datafile = 0; // Set for 3.1.1 and 3.1.2 datafiles
#ifdef NO_MP3_PLAYER
#define SPECIAL_VERSION "NMP"
#else
#define SPECIAL_VERSION ""
#endif
// Version and build numbers
#define AC_VERSION_TEXT "3.21 "
#define ACI_VERSION_TEXT "3.21.1115"SPECIAL_VERSION
// this needs to be updated if the "play" struct changes
#define LOWEST_SGVER_COMPAT "3.20.1103"SPECIAL_VERSION
//#define THIS_IS_THE_ENGINE now defined in the VC Project so that it's defined in all files
#define UNICODE
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#ifdef DJGPP
#include <dir.h>
#endif
#if !defined(LINUX_VERSION) && !defined(MAC_VERSION)
#include <process.h>
#endif
// MACPORT FIX: endian support
#include "bigend.h"
#ifdef ALLEGRO_BIG_ENDIAN
struct DialogTopic;
void preprocess_dialog_script(DialogTopic *);
#endif
// Old dialog support
unsigned char** old_dialog_scripts;
char** old_speech_lines;
#ifdef MAC_VERSION
char dataDirectory[512];
char appDirectory[512];
extern "C"
{
int osx_sys_question(const char *msg, const char *but1, const char *but2);
}
#endif
#include "misc.h"
// This is needed by a couple of headers, so it's at the top
extern "C" {
extern long cliboffset(char*);
}
extern char lib_file_name[];
/*
extern "C" {
extern void * memcpy_amd(void *dest, const void *src, size_t n);
}
#define memcpyfast memcpy_amd*/
#define memcpyfast memcpy
#define USE_CLIB
#define IS_ANTIALIAS_SPRITES usetup.enable_antialiasing && (play.disable_antialiasing == 0)
extern int our_eip;
#include "wgt2allg.h"
#include "sprcache.h"
// Allegro 4 has switched 15-bit colour to BGR instead of RGB, so
// in this case we need to convert the graphics on load
#if ALLEGRO_DATE > 19991010
#define USE_15BIT_FIX
#endif
#ifdef WINDOWS_VERSION
#include <crtdbg.h>
#include "winalleg.h"
#include <shlwapi.h>
#elif defined(LINUX_VERSION) || defined(MAC_VERSION)
#define HWND long
#define _getcwd getcwd
#define strnicmp strncasecmp
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "../PSP/launcher/pe.h"
long int filelength(int fhandle)
{
struct stat statbuf;
fstat(fhandle, &statbuf);
return statbuf.st_size;
}
#else // it's DOS (DJGPP)
#include "sys/exceptn.h"
#define _getcwd getcwd
int sys_getch() {
return getch();
}
#endif // WINDOWS_VERSION
#define getr32(xx) ((xx >> _rgb_r_shift_32) & 0xFF)
#define getg32(xx) ((xx >> _rgb_g_shift_32) & 0xFF)
#define getb32(xx) ((xx >> _rgb_b_shift_32) & 0xFF)
#define geta32(xx) ((xx >> _rgb_a_shift_32) & 0xFF)
#define makeacol32(r,g,b,a) ((r << _rgb_r_shift_32) | (g << _rgb_g_shift_32) | (b << _rgb_b_shift_32) | (a << _rgb_a_shift_32))
/*
#if defined(WINDOWS_VERSION) || defined(LINUX_VERSION) || defined(MAC_VERSION)
not needed now that allegro is being built with MSVC solution with no ASM
// The assembler stretch routine seems to GPF
extern "C" {
void Cstretch_sprite(BITMAP *dst, BITMAP *src, int x, int y, int w, int h);
void Cstretch_blit(BITMAP *src, BITMAP *dst, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh);
}
#define stretch_sprite Cstretch_sprite
#define stretch_blit Cstretch_blit
#endif // WINDOWS_VERSION || LINUX_VERSION || MAC_VERSION
*/
void draw_sprite_compensate(int,int,int,int);
char *get_translation(const char*);
int source_text_length = -1;
#define NO_SAVE_FUNCTIONS
#define LOADROOM_DO_POLL
#include "acroom.h"
#include "cscomp.h"
#include <aastr.h>
#include <acdebug.h>
#define INI_READONLY
//#include <myini.H>
#include "agsplugin.h"
#include <apeg.h>
// We need COLOR_DEPTH_24 to allow it to load the preload PCX in hi-col
BEGIN_COLOR_DEPTH_LIST
COLOR_DEPTH_8
COLOR_DEPTH_15
COLOR_DEPTH_16
COLOR_DEPTH_24
COLOR_DEPTH_32
END_COLOR_DEPTH_LIST
#if defined(WINDOWS_VERSION) && !defined(_DEBUG)
#define USE_CUSTOM_EXCEPTION_HANDLER
#endif
extern "C" HWND allegro_wnd;
// archive attributes to search for - al_findfirst breaks with 0
#define FA_SEARCH -1
// MACPORT FIX 9/6/5: undef M_PI first
#undef M_PI
#define M_PI 3.14159265358979323846
// Windows Vista Rich Save Games, modified to be platform-agnostic
#define RM_MAXLENGTH 1024
#define RM_MAGICNUMBER "RGMH"
#pragma pack(push)
#pragma pack(1)
typedef struct _RICH_GAME_MEDIA_HEADER
{
long dwMagicNumber;
long dwHeaderVersion;
long dwHeaderSize;
long dwThumbnailOffsetLowerDword;
long dwThumbnailOffsetHigherDword;
long dwThumbnailSize;
unsigned char guidGameId[16];
unsigned short szGameName[RM_MAXLENGTH];
unsigned short szSaveName[RM_MAXLENGTH];
unsigned short szLevelName[RM_MAXLENGTH];
unsigned short szComments[RM_MAXLENGTH];
} RICH_GAME_MEDIA_HEADER;
#pragma pack(pop)
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#ifdef WINDOWS_VERSION
int wArgc;
LPWSTR *wArgv;
#else
#define wArgc argc
#define wArgv argv
#define LPWSTR char*
#define LPCWSTR const char*
#define WCHAR char
#define StrCpyW strcpy
#endif
// ***** EXTERNS ****
extern "C" {
extern int csetlib(char*,char*);
extern FILE*clibfopen(char*,char*);
extern int cfopenpriority;
}
extern int minstalled();
extern void mnewcursor(char);
extern void mgetgraphpos();
extern void mloadwcursor(char*);
extern int misbuttondown(int);
extern int disable_mgetgraphpos;
extern void msetcallback(IMouseGetPosCallback *gpCallback);
extern void msethotspot(int,int);
extern int ismouseinbox(int,int,int,int);
extern void print_welcome_text(char*,char*);
extern char currentcursor;
extern int mousex,mousey;
extern block mousecurs[10];
extern int hotx, hoty;
extern char*get_language_text(int);
extern void init_language_text(char*);
extern int loadgamedialog();
extern int savegamedialog();
extern int quitdialog();
extern int cbuttfont;
extern int acdialog_font;
extern int enternumberwindow(char*);
extern void enterstringwindow(char*,char*);
extern int roomSelectorWindow(int currentRoom, int numRooms, int*roomNumbers, char**roomNames);
extern void ccFlattenGlobalData (ccInstance *);
extern void ccUnFlattenGlobalData (ccInstance *);
// CD Player functions
// flags returned with cd_getstatus
#define CDS_DRIVEOPEN 0x0001 // tray is open
#define CDS_DRIVELOCKED 0x0002 // tray locked shut by software
#define CDS_AUDIOSUPPORT 0x0010 // supports audio CDs
#define CDS_DRIVEEMPTY 0x0800 // no CD in drive
// function definitions
extern int cd_installed();
extern int cd_getversion();
extern int cd_getdriveletters(char*);
extern void cd_driverinit(int);
extern void cd_driverclose(int);
extern long cd_getstatus(int);
extern void cd_playtrack(int,int);
extern void cd_stopmusic(int);
extern void cd_resumemusic(int);
extern void cd_eject(int);
extern void cd_uneject(int);
extern int cd_getlasttrack(int);
extern int cd_isplayingaudio(int);
extern void QGRegisterFunctions(); // let the QFG module register its own
int eip_guinum, eip_guiobj;
int trans_mode=0;
int engineNeedsAsInt = 100;
int sc_GetTime(int whatti);
void quitprintf(char*texx, ...);
void replace_macro_tokens(char*,char*);
void wouttext_reverseifnecessary(int x, int y, int font, char *text);
void SetGameSpeed(int newspd);
void SetMultitasking(int mode);
void put_sprite_256(int xxx,int yyy,block piccy);
void construct_virtual_screen(bool fullRedraw);
int initialize_engine_with_exception_handling(int argc,char*argv[]);
int initialize_engine(int argc,char*argv[]);
block recycle_bitmap(block bimp, int coldep, int wid, int hit);
#define WOUTTEXT_REVERSE wouttext_reverseifnecessary
#include "acgui.h"
#include "acruntim.h"
#include "acsound.h"
#define MAX_SCRIPT_MODULES 50
// **** TYPES ****
// 64 bit: This struct must be 8 byte long
struct ScriptGUI {
int id;
//GUIMain *gui;
int __padding;
};
struct ScriptHotspot {
int id;
int reserved;
};
struct ScriptRegion {
int id;
int reserved;
};
// JJS: Replacement for the global roomstats array in the original engine.
RoomStatus* room_statuses[MAX_ROOMS];
// Replaces all accesses to the roomstats array
RoomStatus* getRoomStatus(int room)
{
if (room_statuses[room] == NULL)
{
// First access, allocate and initialise the status
room_statuses[room] = new RoomStatus;
memset(room_statuses[room], 0, sizeof(RoomStatus));
}
return room_statuses[room];
}
// Used in places where it is only important to know whether the player
// had previously entered the room. In this case it is not necessary
// to initialise the status because a player can only have been in
// a room if the status is already initialised.
bool isRoomStatusValid(int room)
{
return (room_statuses[room] != NULL);
}
void resetRoomStatuses()
{
for (int i = 0; i < MAX_ROOMS; i++)
{
if (room_statuses[i] != NULL)
{
if ((room_statuses[i]->tsdata != NULL) && (room_statuses[i]->tsdatasize > 0))
free(room_statuses[i]->tsdata);
// Don't delete the status on 2.x. The status struct contains NewInteraction
// pointer that are also referenced in the current room struct.
// If they are freed here this will lead to an access violation when the
// room unloading function tries to frees them.
if (loaded_game_file_version <= 32)
{
room_statuses[i]->tsdatasize = 0;
room_statuses[i]->tsdata = 0;
room_statuses[i]->beenhere = 0;
}
else
{
delete room_statuses[i];
room_statuses[i] = NULL;
}
}
}
}
int ExecutingScript::queue_action(PostScriptAction act, int data, const char *aname) {
if (numPostScriptActions >= MAX_QUEUED_ACTIONS)
quitprintf("!%s: Cannot queue action, post-script queue full", aname);
if (numPostScriptActions > 0) {
// if something that will terminate the room has already
// been queued, don't allow a second thing to be queued
switch (postScriptActions[numPostScriptActions - 1]) {
case ePSANewRoom:
case ePSARestoreGame:
case ePSARestoreGameDialog:
case ePSARunAGSGame:
case ePSARestartGame:
quitprintf("!%s: Cannot run this command, since there is a %s command already queued to run", aname, postScriptActionNames[numPostScriptActions - 1]);
break;
// MACPORT FIX 9/6/5: added default clause to remove warning
default:
break;
}
}
postScriptActions[numPostScriptActions] = act;
postScriptActionData[numPostScriptActions] = data;
postScriptActionNames[numPostScriptActions] = aname;
numPostScriptActions++;
return numPostScriptActions - 1;
}
void ExecutingScript::run_another (char *namm, int p1, int p2) {
if (numanother < MAX_QUEUED_SCRIPTS)
numanother++;
else {
/*debug_log("Warning: too many scripts to run, ignored %s(%d,%d)",
script_run_another[numanother - 1], run_another_p1[numanother - 1],
run_another_p2[numanother - 1]);*/
}
int thisslot = numanother - 1;
strcpy(script_run_another[thisslot], namm);
run_another_p1[thisslot] = p1;
run_another_p2[thisslot] = p2;
}
void ExecutingScript::init() {
inst = NULL;
forked = 0;
numanother = 0;
numPostScriptActions = 0;
}
ExecutingScript::ExecutingScript() {
init();
}
struct TempEip {
int oldval;
TempEip (int newval) {
oldval = our_eip;
our_eip = newval;
}
~TempEip () { our_eip = oldval; }
};
struct DebugConsoleText {
char text[100];
char script[12];
};
struct CachedActSpsData {
int xWas, yWas;
int baselineWas;
int isWalkBehindHere;
int valid;
};
struct NonBlockingScriptFunction
{
const char* functionName;
int numParameters;
void* param1;
void* param2;
bool roomHasFunction;
bool globalScriptHasFunction;
bool moduleHasFunction[MAX_SCRIPT_MODULES];
bool atLeastOneImplementationExists;
NonBlockingScriptFunction(const char*funcName, int numParams)
{
this->functionName = funcName;
this->numParameters = numParams;
atLeastOneImplementationExists = false;
roomHasFunction = true;
globalScriptHasFunction = true;
for (int i = 0; i < MAX_SCRIPT_MODULES; i++)
{
moduleHasFunction[i] = true;
}
}
};
// **** GLOBALS ****
char *music_file;
char *speech_file;
WCHAR directoryPathBuffer[MAX_PATH];
/*extern int get_route_composition();
extern int routex1;*/
extern char*scripttempn;
#define REC_MOUSECLICK 1
#define REC_MOUSEMOVE 2
#define REC_MOUSEDOWN 3
#define REC_KBHIT 4
#define REC_GETCH 5
#define REC_KEYDOWN 6
#define REC_MOUSEWHEEL 7
#define REC_SPEECHFINISHED 8
#define REC_ENDOFFILE 0x6f
short *recordbuffer = NULL;
int recbuffersize = 0, recsize = 0;
volatile int switching_away_from_game = 0;
// PSP: Update in thread if wanted.
extern volatile int psp_audio_multithreaded;
volatile bool update_mp3_thread_running = false;
int musicPollIterator; // long name so it doesn't interfere with anything else
#define UPDATE_MP3_THREAD \
while (switching_away_from_game) { } \
for (musicPollIterator = 0; musicPollIterator <= MAX_SOUND_CHANNELS; musicPollIterator++) { \
if ((channels[musicPollIterator] != NULL) && (channels[musicPollIterator]->done == 0)) \
channels[musicPollIterator]->poll(); \
}
#define UPDATE_MP3 \
if (!psp_audio_multithreaded) \
{ UPDATE_MP3_THREAD }
//#define UPDATE_MP3 update_polled_stuff();
#if defined(PSP_VERSION)
// PSP: Workaround for sound stuttering. Do sound updates in its own thread.
int update_mp3_thread(SceSize args, void *argp)
{
while (update_mp3_thread_running)
{
UPDATE_MP3_THREAD
sceKernelDelayThread(1000 * 50);
}
return 0;
}
#elif (defined(LINUX_VERSION) && !defined(PSP_VERSION)) || defined(MAC_VERSION)
void* update_mp3_thread(void* arg)
{
while (update_mp3_thread_running)
{
UPDATE_MP3_THREAD
usleep(1000 * 50);
}
pthread_exit(NULL);
}
#elif defined(WINDOWS_VERSION)
DWORD WINAPI update_mp3_thread(LPVOID lpParam)
{
while (update_mp3_thread_running)
{
UPDATE_MP3_THREAD
Sleep(50);
}
return 0;
}
#endif
const char* sgnametemplate = "agssave.%03d";
char saveGameSuffix[MAX_SG_EXT_LENGTH + 1];
SOUNDCLIP *channels[MAX_SOUND_CHANNELS+1];
SOUNDCLIP *cachedQueuedMusic = NULL;
int numSoundChannels = 8;
#define SCHAN_SPEECH 0
#define SCHAN_AMBIENT 1
#define SCHAN_MUSIC 2
#define SCHAN_NORMAL 3
#define AUDIOTYPE_LEGACY_AMBIENT_SOUND 1
#define AUDIOTYPE_LEGACY_MUSIC 2
#define AUDIOTYPE_LEGACY_SOUND 3
#define MAX_ANIMATING_BUTTONS 15
#define RESTART_POINT_SAVE_GAME_NUMBER 999
enum WalkBehindMethodEnum
{
DrawOverCharSprite,
DrawAsSeparateSprite,
DrawAsSeparateCharSprite
};
AGSPlatformDriver *platform = NULL;
// crossFading is >0 (channel number of new track), or -1 (old
// track fading out, no new track)
int crossFading = 0, crossFadeVolumePerStep = 0, crossFadeStep = 0;
int crossFadeVolumeAtStart = 0;
int last_sound_played[MAX_SOUND_CHANNELS + 1];
char *heightTestString = "ZHwypgfjqhkilIK";
block virtual_screen;
int scrnwid,scrnhit;
int current_screen_resolution_multiplier = 1;
roomstruct thisroom;
GameSetupStruct game;
RoomStatus troom; // used for non-saveable rooms, eg. intro
GameState play;
GameSetup usetup;
CharacterExtras *charextra;
int force_letterbox = 0;
int game_paused=0;
int ifacepopped=-1; // currently displayed pop-up GUI (-1 if none)
color palette[256];
//block spriteset[MAX_SPRITES+1];
//SpriteCache spriteset (MAX_SPRITES+1);
// initially size 1, this will be increased by the initFile function
SpriteCache spriteset(1);
long t1; // timer for FPS
int cur_mode,cur_cursor;
int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
char saveGameDirectory[260] = "./";
//int abort_all_conditions=0;
int fps=0,display_fps=0;
DebugConsoleText debug_line[DEBUG_CONSOLE_NUMLINES];
int first_debug_line = 0, last_debug_line = 0, display_console = 0;
char *walkBehindExists = NULL; // whether a WB area is in this column
int *walkBehindStartY = NULL, *walkBehindEndY = NULL;
char noWalkBehindsAtAll = 0;
int walkBehindLeft[MAX_OBJ], walkBehindTop[MAX_OBJ];
int walkBehindRight[MAX_OBJ], walkBehindBottom[MAX_OBJ];
IDriverDependantBitmap *walkBehindBitmap[MAX_OBJ];
int walkBehindsCachedForBgNum = 0;
WalkBehindMethodEnum walkBehindMethod = DrawOverCharSprite;
unsigned int loopcounter=0,lastcounter=0;
volatile unsigned long globalTimerCounter = 0;
char alpha_blend_cursor = 0;
RoomObject*objs;
RoomStatus*croom=NULL;
CharacterInfo*playerchar;
long _sc_PlayerCharPtr = 0;
int offsetx = 0, offsety = 0;
int use_extra_sound_offset = 0;
GUIMain*guis=NULL;
//GUIMain dummygui;
//GUIButton dummyguicontrol;
block *guibg = NULL;
IDriverDependantBitmap **guibgbmp = NULL;
ccScript* gamescript=NULL;
ccScript* dialogScriptsScript = NULL;
ccInstance *gameinst = NULL, *roominst = NULL;
ccInstance *dialogScriptsInst = NULL;
ccInstance *gameinstFork = NULL, *roominstFork = NULL;
IGraphicsDriver *gfxDriver;
IDriverDependantBitmap *mouseCursor = NULL;
IDriverDependantBitmap *blankImage = NULL;
IDriverDependantBitmap *blankSidebarImage = NULL;
IDriverDependantBitmap *debugConsole = NULL;
block debugConsoleBuffer = NULL;
block blank_mouse_cursor = NULL;
bool current_background_is_dirty = false;
int longestline = 0;
PluginObjectReader pluginReaders[MAX_PLUGIN_OBJECT_READERS];
int numPluginReaders = 0;
ccScript *scriptModules[MAX_SCRIPT_MODULES];
ccInstance *moduleInst[MAX_SCRIPT_MODULES];
ccInstance *moduleInstFork[MAX_SCRIPT_MODULES];
char *moduleRepExecAddr[MAX_SCRIPT_MODULES];
int numScriptModules = 0;
ViewStruct*views=NULL;
ScriptMouse scmouse;
COLOR_MAP maincoltable;
ScriptSystem scsystem;
block _old_screen=NULL;
block _sub_screen=NULL;
MoveList *mls = NULL;
DialogTopic *dialog;
block walkareabackup=NULL, walkable_areas_temp = NULL;
ExecutingScript scripts[MAX_SCRIPT_AT_ONCE];
ExecutingScript*curscript = NULL;
AnimatingGUIButton animbuts[MAX_ANIMATING_BUTTONS];
int numAnimButs = 0;
int num_scripts=0, eventClaimed = EVENT_NONE;
int getloctype_index = 0, getloctype_throughgui = 0;
long user_disabled_for=0,user_disabled_data=0,user_disabled_data2=0;
int user_disabled_data3=0;
int is_complete_overlay=0,is_text_overlay=0;
// Sierra-style speech settings
int face_talking=-1,facetalkview=0,facetalkwait=0,facetalkframe=0;
int facetalkloop=0, facetalkrepeat = 0, facetalkAllowBlink = 1;
int facetalkBlinkLoop = 0;
CharacterInfo *facetalkchar = NULL;
// lip-sync speech settings
int loops_per_character, text_lips_offset, char_speaking = -1;
char *text_lips_text = NULL;
SpeechLipSyncLine *splipsync = NULL;
int numLipLines = 0, curLipLine = -1, curLipLinePhenome = 0;
int gameHasBeenRestored = 0;
char **characterScriptObjNames = NULL;
char objectScriptObjNames[MAX_INIT_SPR][MAX_SCRIPT_NAME_LEN + 5];
char **guiScriptObjNames = NULL;
// set to 0 once successful
int working_gfx_mode_status = -1;
int said_speech_line; // used while in dialog to track whether screen needs updating
int restrict_until=0;
int gs_to_newroom=-1;
ScreenOverlay screenover[MAX_SCREEN_OVERLAYS];
int proper_exit=0,our_eip=0;
int numscreenover=0;
int scaddr;
int walk_behind_baselines_changed = 0;
int displayed_room=-10,starting_room = -1;
int mouse_on_iface=-1; // mouse cursor is over this interface
int mouse_on_iface_button=-1;
int mouse_pushed_iface=-1; // this BUTTON on interface MOUSE_ON_IFACE is pushed
int mouse_ifacebut_xoffs=-1,mouse_ifacebut_yoffs=-1;
int debug_flags=0;
IDriverDependantBitmap* roomBackgroundBmp = NULL;
int use_compiled_folder_as_current_dir = 0;
int editor_debugging_enabled = 0;
int editor_debugging_initialized = 0;
char editor_debugger_instance_token[100];
IAGSEditorDebugger *editor_debugger = NULL;
int break_on_next_script_step = 0;
volatile int game_paused_in_debugger = 0;
HWND editor_window_handle = NULL;
int in_enters_screen=0,done_es_error = 0;
int in_leaves_screen = -1;
int need_to_stop_cd=0;
int debug_15bit_mode = 0, debug_24bit_mode = 0;
int said_text = 0;
int convert_16bit_bgr = 0;
int mouse_z_was = 0;
int bg_just_changed = 0;
int loaded_game_file_version = 0;
volatile char want_exit = 0, abort_engine = 0;
char check_dynamic_sprites_at_exit = 1;
#define DBG_NOIFACE 1
#define DBG_NODRAWSPRITES 2
#define DBG_NOOBJECTS 4
#define DBG_NOUPDATE 8
#define DBG_NOSFX 0x10
#define DBG_NOMUSIC 0x20
#define DBG_NOSCRIPT 0x40
#define DBG_DBGSCRIPT 0x80
#define DBG_DEBUGMODE 0x100
#define DBG_REGONLY 0x200
#define DBG_NOVIDEO 0x400
#define MAXEVENTS 15
EventHappened event[MAXEVENTS+1];
int numevents=0;
#define EV_TEXTSCRIPT 1
#define EV_RUNEVBLOCK 2
#define EV_FADEIN 3
#define EV_IFACECLICK 4
#define EV_NEWROOM 5
#define TS_REPEAT 1
#define TS_KEYPRESS 2
#define TS_MCLICK 3
#define EVB_HOTSPOT 1
#define EVB_ROOM 2
char ac_engine_copyright[]="Adventure Game Studio engine & tools (c) 1999-2000 by Chris Jones.";
int current_music_type = 0;
#define LOCTYPE_HOTSPOT 1
#define LOCTYPE_CHAR 2
#define LOCTYPE_OBJ 3
#define MAX_DYNAMIC_SURFACES 20
#define REP_EXEC_ALWAYS_NAME "repeatedly_execute_always"
#define REP_EXEC_NAME "repeatedly_execute"
char*tsnames[4]={NULL, REP_EXEC_NAME, "on_key_press","on_mouse_click"};
char*evblockbasename;
int evblocknum;
//int current_music=0;
int frames_per_second=40;
int in_new_room=0, new_room_was = 0; // 1 in new room, 2 first time in new room, 3 loading saved game
int new_room_pos=0;
int new_room_x = SCR_NO_VALUE, new_room_y = SCR_NO_VALUE;
unsigned int load_new_game = 0;
int load_new_game_restore = -1;
int inside_script=0,in_graph_script=0;
int no_blocking_functions = 0; // set to 1 while in rep_Exec_always
int in_inv_screen = 0, inv_screen_newroom = -1;
int mouse_frame=0,mouse_delay=0;
int lastmx=-1,lastmy=-1;
int new_room_flags=0;
#define MAX_SPRITES_ON_SCREEN 76
SpriteListEntry sprlist[MAX_SPRITES_ON_SCREEN];
int sprlistsize=0;
#define MAX_THINGS_TO_DRAW 125
SpriteListEntry thingsToDrawList[MAX_THINGS_TO_DRAW];
int thingsToDrawSize = 0;
int use_cdplayer=0;
bool triedToUseCdAudioCommand = false;
int final_scrn_wid=0,final_scrn_hit=0,final_col_dep=0;
int post_script_cleanup_stack = 0;
// actsps is used for temporary storage of the bitamp image
// of the latest version of the sprite
int actSpsCount = 0;
block *actsps;
IDriverDependantBitmap* *actspsbmp;
// temporary cache of walk-behind for this actsps image
block *actspswb;
IDriverDependantBitmap* *actspswbbmp;
CachedActSpsData* actspswbcache;
CharacterCache *charcache = NULL;
ObjectCache objcache[MAX_INIT_SPR];
ScriptObject scrObj[MAX_INIT_SPR];
ScriptGUI *scrGui = NULL;
ScriptHotspot scrHotspot[MAX_HOTSPOTS];
ScriptRegion scrRegion[MAX_REGIONS];
ScriptInvItem scrInv[MAX_INV];
ScriptDialog scrDialog[MAX_DIALOG];
RGB_MAP rgb_table; // for 256-col antialiasing
char* game_file_name=NULL;
int want_quit = 0, screen_reset = 0;
block raw_saved_screen = NULL;
block dotted_mouse_cursor = NULL;
block dynamicallyCreatedSurfaces[MAX_DYNAMIC_SURFACES];
int scrlockWasDown = 0;
// whether there are currently remnants of a DisplaySpeech
int screen_is_dirty = 0;
char replayfile[MAX_PATH] = "record.dat";
int replay_time = 0;
unsigned long replay_last_second = 0;
int replay_start_this_time = 0;
char pexbuf[STD_BUFFER_SIZE];
int pluginsWantingDebugHooks = 0;
const char *replayTempFile = "~replay.tmp";
NonBlockingScriptFunction repExecAlways(REP_EXEC_ALWAYS_NAME, 0);
NonBlockingScriptFunction getDialogOptionsDimensionsFunc("dialog_options_get_dimensions", 1);
NonBlockingScriptFunction renderDialogOptionsFunc("dialog_options_render", 1);
NonBlockingScriptFunction getDialogOptionUnderCursorFunc("dialog_options_get_active", 1);
NonBlockingScriptFunction runDialogOptionMouseClickHandlerFunc("dialog_options_mouse_click", 2);
// *** FUNCTIONS ****
bool AmbientSound::IsPlaying () {
if (channel <= 0)
return false;
return (channels[channel] != NULL) ? true : false;
}
int Overlay_GetValid(ScriptOverlay *scover);
void mainloop(bool checkControls = false, IDriverDependantBitmap *extraBitmap = NULL, int extraX = 0, int extraY = 0);
void set_mouse_cursor(int);
void set_default_cursor();
int run_text_script(ccInstance*,char*);
int run_text_script_2iparam(ccInstance*,char*,long,long);
int run_text_script_iparam(ccInstance*,char*,long);
//void run_graph_script(int);
//void run_event_block(EventBlock*,int,int=-1, int=-1);
int run_interaction_event (NewInteraction *nint, int evnt, int chkAny = -1, int isInv = 0);
int run_interaction_script(InteractionScripts *nint, int evnt, int chkAny = -1, int isInv = 0);
void new_room(int,CharacterInfo*);
void NewRoom(int);
void AnimateObject(int,int,int,int);
void SetObjectView(int,int);
void GiveScore(int);
void walk_character(int,int,int,int,bool);
void move_object(int,int,int,int,int);
void StopMoving(int);
void MoveCharacterToHotspot(int,int);
int GetCursorMode();
void GetLocationName(int,int,char*);
void save_game(int,const char*);
int load_game(int,char*, int*);
void update_music_volume();
int invscreen();
void process_interface_click(int,int,int);
void DisplayMessage (int);
void do_conversation(int);
void compile_room_script();
int CreateTextOverlay(int,int,int,int,int,char*,...);
void RemoveOverlay(int);
void stopmusic();
void play_flc_file(int,int);
void SetCharacterView(int,int);
void ReleaseCharacterView(int);
void setevent(int evtyp,int ev1=0,int ev2=-1000,int ev3=0);
void update_events();
void process_event(EventHappened*);
int GetLocationType(int,int);
int __GetLocationType(int,int,int);
int AreCharObjColliding(int charid,int objid);
int play_speech(int,int);
void stop_speech();
int play_sound (int);
int play_sound_priority (int, int);
int __Rand(int);
int cd_manager(int,int);
int DisplaySpeechBackground(int,char*);
void MergeObject(int);
void script_debug(int,int);
void sc_inputbox(const char*,char*);
void ParseText(char*);
void FaceLocation(int,int,int);
void check_debug_keys();
int IsInterfaceEnabled();
void break_up_text_into_lines(int,int,char*);
void start_game();
void init_game_settings();
void show_preload();
void stop_recording ();
void save_game_data (FILE *, block screenshot);
void setup_script_exports ();
void SetSpeechFont (int);
void SetNormalFont (int);
void tint_image (block source, block dest, int red, int grn, int blu, int light_level, int luminance=255);
void get_message_text (int msnum, char *buffer, char giveErr = 1);
void render_graphics(IDriverDependantBitmap *extraBitmap = NULL, int extraX = 0, int extraY = 0);
int wait_loop_still_valid();
SOUNDCLIP *load_music_from_disk(int mnum, bool repeat);
void play_new_music(int mnum, SOUNDCLIP *music);
int GetGameSpeed();
int check_for_messages_from_editor();
int show_dialog_options(int dlgnum, int sayChosenOption, bool runGameLoopsInBackground);
void add_to_sprite_list(IDriverDependantBitmap* spp, int xx, int yy, int baseline, int trans, int sprNum, bool isWalkBehind = false);
// MACPORT FIX 9/6/5: undef (was macro) and add prototype
#undef wouttext_outline
void wouttext_outline(int xxp, int yyp, int usingfont, char *texx);
#define Random __Rand
#define ALLEGRO_KEYBOARD_HANDLER
// KEYBOARD HANDLER
#if defined(LINUX_VERSION) || defined(MAC_VERSION)
int myerrno;
#else
int errno;
#define myerrno errno
#endif
#if defined(MAC_VERSION) && !defined(IOS_VERSION)
#define EXTENDED_KEY_CODE 0x3f
#else
#define EXTENDED_KEY_CODE 0
#endif
#define AGS_KEYCODE_INSERT 382
#define AGS_KEYCODE_DELETE 383
#define AGS_KEYCODE_ALT_TAB 399
#define READKEY_CODE_ALT_TAB 0x4000
int my_readkey() {
int gott=readkey();
int scancode = ((gott >> 8) & 0x00ff);
if (gott == READKEY_CODE_ALT_TAB)
{
// Alt+Tab, it gets stuck down unless we do this
return AGS_KEYCODE_ALT_TAB;
}
/* char message[200];
sprintf(message, "Scancode: %04X", gott);
OutputDebugString(message);*/
/*if ((scancode >= KEY_0_PAD) && (scancode <= KEY_9_PAD)) {
// fix numeric pad keys if numlock is off (allegro 4.2 changed this behaviour)
if ((key_shifts & KB_NUMLOCK_FLAG) == 0)
gott = (gott & 0xff00) | EXTENDED_KEY_CODE;
}*/
if ((gott & 0x00ff) == EXTENDED_KEY_CODE) {
gott = scancode + 300;
// convert Allegro KEY_* numbers to scan codes
// (for backwards compatibility we can't just use the
// KEY_* constants now, it's too late)
if ((gott>=347) & (gott<=356)) gott+=12;
// F11-F12
else if ((gott==357) || (gott==358)) gott+=76;
// insert / numpad insert
else if ((scancode == KEY_0_PAD) || (scancode == KEY_INSERT))
gott = AGS_KEYCODE_INSERT;
// delete / numpad delete
else if ((scancode == KEY_DEL_PAD) || (scancode == KEY_DEL))
gott = AGS_KEYCODE_DELETE;
// Home
else if (gott == 378) gott = 371;
// End
else if (gott == 379) gott = 379;
// PgUp
else if (gott == 380) gott = 373;
// PgDn
else if (gott == 381) gott = 381;
// left arrow
else if (gott==382) gott=375;
// right arrow
else if (gott==383) gott=377;
// up arrow
else if (gott==384) gott=372;
// down arrow
else if (gott==385) gott=380;
// numeric keypad
else if (gott==338) gott=379;
else if (gott==339) gott=380;
else if (gott==340) gott=381;
else if (gott==341) gott=375;
else if (gott==342) gott=376;
else if (gott==343) gott=377;
else if (gott==344) gott=371;
else if (gott==345) gott=372;
else if (gott==346) gott=373;
}
else
gott = gott & 0x00ff;
// Alt+X, abort (but only once game is loaded)
if ((gott == play.abort_key) && (displayed_room >= 0)) {
check_dynamic_sprites_at_exit = 0;
quit("!|");
}
//sprintf(message, "Keypress: %d", gott);
//OutputDebugString(message);
return gott;
}
//#define getch() my_readkey()
//#undef kbhit
//#define kbhit keypressed
// END KEYBOARD HANDLER
// for external modules to call
void next_iteration() {
NEXT_ITERATION();
}
void write_record_event (int evnt, int dlen, short *dbuf) {
recordbuffer[recsize] = play.gamestep;
recordbuffer[recsize+1] = evnt;
for (int i = 0; i < dlen; i++)
recordbuffer[recsize + i + 2] = dbuf[i];
recsize += dlen + 2;
if (recsize >= recbuffersize - 100) {
recbuffersize += 10000;
recordbuffer = (short*)realloc (recordbuffer, recbuffersize * sizeof(short));
}
play.gamestep++;
}
void disable_replay_playback () {
play.playback = 0;
if (recordbuffer)
free (recordbuffer);
recordbuffer = NULL;
disable_mgetgraphpos = 0;
}
void done_playback_event (int size) {
recsize += size;
play.gamestep++;
if ((recsize >= recbuffersize) || (recordbuffer[recsize+1] == REC_ENDOFFILE))
disable_replay_playback();
}
int rec_getch () {
if (play.playback) {
if ((recordbuffer[recsize] == play.gamestep) && (recordbuffer[recsize + 1] == REC_GETCH)) {
int toret = recordbuffer[recsize + 2];
done_playback_event (3);
return toret;
}
// Since getch() waits for a key to be pressed, if we have no
// record for it we're out of sync
quit("out of sync in playback in getch");
}
int result = my_readkey();
if (play.recording) {
short buff[1] = {result};
write_record_event (REC_GETCH, 1, buff);
}
return result;
}
int rec_kbhit () {
if ((play.playback) && (recordbuffer != NULL)) {
// check for real keypresses to abort the replay
if (keypressed()) {
if (my_readkey() == 27) {
disable_replay_playback();
return 0;
}
}
// now simulate the keypresses
if ((recordbuffer[recsize] == play.gamestep) && (recordbuffer[recsize + 1] == REC_KBHIT)) {
done_playback_event (2);
return 1;
}
return 0;
}
int result = keypressed();
if ((result) && (globalTimerCounter < play.ignore_user_input_until_time))
{
// ignoring user input
my_readkey();
result = 0;
}
if ((result) && (play.recording)) {
write_record_event (REC_KBHIT, 0, NULL);
}
return result;
}
char playback_keystate[KEY_MAX];
int rec_iskeypressed (int keycode) {
if (play.playback) {
if ((recordbuffer[recsize] == play.gamestep)
&& (recordbuffer[recsize + 1] == REC_KEYDOWN)
&& (recordbuffer[recsize + 2] == keycode)) {
playback_keystate[keycode] = recordbuffer[recsize + 3];
done_playback_event (4);
}
return playback_keystate[keycode];
}
int toret = key[keycode];
if (play.recording) {
if (toret != playback_keystate[keycode]) {
short buff[2] = {keycode, toret};
write_record_event (REC_KEYDOWN, 2, buff);
playback_keystate[keycode] = toret;
}
}
return toret;
}
int rec_isSpeechFinished () {
if (play.playback) {
if ((recordbuffer[recsize] == play.gamestep) && (recordbuffer[recsize + 1] == REC_SPEECHFINISHED)) {
done_playback_event (2);
return 1;
}
return 0;
}
if (!channels[SCHAN_SPEECH]->done) {
return 0;
}
if (play.recording)
write_record_event (REC_SPEECHFINISHED, 0, NULL);
return 1;
}
int recbutstate[4] = {-1, -1, -1, -1};
int rec_misbuttondown (int but) {
if (play.playback) {
if ((recordbuffer[recsize] == play.gamestep)
&& (recordbuffer[recsize + 1] == REC_MOUSEDOWN)
&& (recordbuffer[recsize + 2] == but)) {
recbutstate[but] = recordbuffer[recsize + 3];
done_playback_event (4);
}
return recbutstate[but];
}
int result = misbuttondown (but);
if (play.recording) {
if (result != recbutstate[but]) {
short buff[2] = {but, result};
write_record_event (REC_MOUSEDOWN, 2, buff);
recbutstate[but] = result;
}
}
return result;
}
int pluginSimulatedClick = NONE;
void PluginSimulateMouseClick(int pluginButtonID) {
pluginSimulatedClick = pluginButtonID - 1;
}
int rec_mgetbutton() {
if ((play.playback) && (recordbuffer != NULL)) {
if ((recordbuffer[recsize] < play.gamestep) && (play.gamestep < 32766))
quit("Playback error: out of sync");
if (loopcounter >= replay_last_second + 40) {
replay_time ++;
replay_last_second += 40;
}
if ((recordbuffer[recsize] == play.gamestep) && (recordbuffer[recsize + 1] == REC_MOUSECLICK)) {
filter->SetMousePosition(recordbuffer[recsize+3], recordbuffer[recsize+4]);
disable_mgetgraphpos = 0;
mgetgraphpos ();
disable_mgetgraphpos = 1;
int toret = recordbuffer[recsize + 2];
done_playback_event (5);
return toret;
}
return NONE;
}
int result;
if (pluginSimulatedClick > NONE) {
result = pluginSimulatedClick;
pluginSimulatedClick = NONE;
}
else {
result = mgetbutton();
}
if ((result >= 0) && (globalTimerCounter < play.ignore_user_input_until_time))
{
// ignoring user input
result = NONE;
}
if (play.recording) {
if (result >= 0) {
short buff[3] = {result, mousex, mousey};
write_record_event (REC_MOUSECLICK, 3, buff);
}
if (loopcounter >= replay_last_second + 40) {
replay_time ++;
replay_last_second += 40;
}
}
return result;
}
void rec_domouse (int what) {
if (play.recording) {
int mxwas = mousex, mywas = mousey;
if (what == DOMOUSE_NOCURSOR)
mgetgraphpos();
else
domouse(what);
if ((mxwas != mousex) || (mywas != mousey)) {
// don't divide down the co-ordinates, because we lose
// the precision, and it might click the wrong thing
// if eg. hi-res 71 -> 35 in record file -> 70 in playback
short buff[2] = {mousex, mousey};
write_record_event (REC_MOUSEMOVE, 2, buff);
}
return;
}
else if ((play.playback) && (recordbuffer != NULL)) {
if ((recordbuffer[recsize] == play.gamestep) && (recordbuffer[recsize + 1] == REC_MOUSEMOVE)) {
filter->SetMousePosition(recordbuffer[recsize+2], recordbuffer[recsize+3]);
disable_mgetgraphpos = 0;
if (what == DOMOUSE_NOCURSOR)
mgetgraphpos();
else
domouse(what);
disable_mgetgraphpos = 1;
done_playback_event (4);
return;
}
}
if (what == DOMOUSE_NOCURSOR)
mgetgraphpos();
else
domouse(what);
}
int check_mouse_wheel () {
if ((play.playback) && (recordbuffer != NULL)) {
if ((recordbuffer[recsize] == play.gamestep) && (recordbuffer[recsize + 1] == REC_MOUSEWHEEL)) {
int toret = recordbuffer[recsize+2];
done_playback_event (3);
return toret;
}
return 0;
}
int result = 0;
if ((mouse_z != mouse_z_was) && (game.options[OPT_MOUSEWHEEL] != 0)) {
if (mouse_z > mouse_z_was)
result = 1;
else
result = -1;
mouse_z_was = mouse_z;
}
if ((play.recording) && (result)) {
short buff[1] = {result};
write_record_event (REC_MOUSEWHEEL, 1, buff);
}
return result;
}
#undef kbhit
#define mgetbutton rec_mgetbutton
#define domouse rec_domouse
#define misbuttondown rec_misbuttondown
#define kbhit rec_kbhit
#define getch rec_getch
void quitprintf(char*texx, ...) {
char displbuf[STD_BUFFER_SIZE];
va_list ap;
va_start(ap,texx);
vsprintf(displbuf,texx,ap);
va_end(ap);
quit(displbuf);
}
void write_log(char*msg) {
/*
FILE*ooo=fopen("ac.log","at");
fprintf(ooo,"%s\n",msg);
fclose(ooo);
*/
platform->WriteDebugString(msg);
}
// this function is only enabled for special builds if a startup
// issue needs to be checked
#define write_log_debug(msg) platform->WriteDebugString(msg)
/*extern "C" {
void write_log_debug(const char*msg) {
//if (play.debug_mode == 0)
//return;
char toWrite[300];
sprintf(toWrite, "%02d/%02d/%04d %02d:%02d:%02d (EIP=%4d) %s", sc_GetTime(4), sc_GetTime(5),
sc_GetTime(6), sc_GetTime(1), sc_GetTime(2), sc_GetTime(3), our_eip, msg);
FILE*ooo=fopen("ac.log","at");
fprintf(ooo,"%s\n", toWrite);
fclose(ooo);
}
}*/
/* The idea of this is that non-essential errors such as "sound file not
found" are logged instead of exiting the program.
*/
void debug_log(char*texx, ...) {
// if not in debug mode, don't print it so we don't worry the
// end player
if (play.debug_mode == 0)
return;
static int first_time = 1;
char displbuf[STD_BUFFER_SIZE];
va_list ap;
va_start(ap,texx);
vsprintf(displbuf,texx,ap);
va_end(ap);
/*if (true) {
char buffer2[STD_BUFFER_SIZE];
strcpy(buffer2, "%");
strcat(buffer2, displbuf);
quit(buffer2);
}*/
char*openmode = "at";
if (first_time) {
openmode = "wt";
first_time = 0;
}
FILE*outfil = fopen("warnings.log",openmode);
if (outfil == NULL)
{
debug_write_console("* UNABLE TO WRITE TO WARNINGS.LOG");
debug_write_console(displbuf);
}
else
{
fprintf(outfil,"(in room %d): %s\n",displayed_room,displbuf);
fclose(outfil);
}
}
void debug_write_console (char *msg, ...) {
char displbuf[STD_BUFFER_SIZE];
va_list ap;
va_start(ap,msg);
vsprintf(displbuf,msg,ap);
va_end(ap);
displbuf[99] = 0;
strcpy (debug_line[last_debug_line].text, displbuf);
ccInstance*curinst = ccGetCurrentInstance();
if (curinst != NULL) {
char scriptname[20];
if (curinst->instanceof == gamescript)
strcpy(scriptname,"G ");
else if (curinst->instanceof == thisroom.compiled_script)
strcpy (scriptname, "R ");
else if (curinst->instanceof == dialogScriptsScript)
strcpy(scriptname,"D ");
else
strcpy(scriptname,"? ");
sprintf(debug_line[last_debug_line].script,"%s%d",scriptname,currentline);
}
else debug_line[last_debug_line].script[0] = 0;
platform->WriteDebugString("%s (%s)", displbuf, debug_line[last_debug_line].script);
last_debug_line = (last_debug_line + 1) % DEBUG_CONSOLE_NUMLINES;
if (last_debug_line == first_debug_line)
first_debug_line = (first_debug_line + 1) % DEBUG_CONSOLE_NUMLINES;
}
#define DEBUG_CONSOLE if (play.debug_mode) debug_write_console
const char *get_cur_script(int numberOfLinesOfCallStack) {
ccGetCallStack(ccGetCurrentInstance(), pexbuf, numberOfLinesOfCallStack);
if (pexbuf[0] == 0)
strcpy(pexbuf, ccErrorCallStack);
return &pexbuf[0];
}
static const char* BREAK_MESSAGE = "BREAK";
struct Breakpoint
{
char scriptName[80];
int lineNumber;
};
DynamicArray<Breakpoint> breakpoints;
int numBreakpoints = 0;
bool send_message_to_editor(const char *msg, const char *errorMsg)
{
const char *callStack = get_cur_script(25);
if (callStack[0] == 0)
return false;
char messageToSend[STD_BUFFER_SIZE];
sprintf(messageToSend, "<?xml version=\"1.0\" encoding=\"Windows-1252\"?><Debugger Command=\"%s\">", msg);
#ifdef WINDOWS_VERSION
sprintf(&messageToSend[strlen(messageToSend)], " <EngineWindow>%d</EngineWindow> ", win_get_window());
#endif
sprintf(&messageToSend[strlen(messageToSend)], " <ScriptState><![CDATA[%s]]></ScriptState> ", callStack);
if (errorMsg != NULL)
{
sprintf(&messageToSend[strlen(messageToSend)], " <ErrorMessage><![CDATA[%s]]></ErrorMessage> ", errorMsg);
}
strcat(messageToSend, "</Debugger>");
editor_debugger->SendMessageToEditor(messageToSend);
return true;
}
bool send_message_to_editor(const char *msg)
{
return send_message_to_editor(msg, NULL);
}
bool init_editor_debugging()
{
#ifdef WINDOWS_VERSION
editor_debugger = GetEditorDebugger(editor_debugger_instance_token);
#else
// Editor isn't ported yet
editor_debugger = NULL;
#endif
if (editor_debugger == NULL)
quit("editor_debugger is NULL but debugger enabled");
if (editor_debugger->Initialize())
{
editor_debugging_initialized = 1;
// Wait for the editor to send the initial breakpoints
// and then its READY message
while (check_for_messages_from_editor() != 2)
{
platform->Delay(10);
}
send_message_to_editor("START");
return true;
}
return false;
}
int check_for_messages_from_editor()
{
if (editor_debugger->IsMessageAvailable())
{
char *msg = editor_debugger->GetNextMessage();
if (msg == NULL)
{
return 0;
}
if (strncmp(msg, "<Engine Command=\"", 17) != 0)
{
//OutputDebugString("Faulty message received from editor:");
//OutputDebugString(msg);
free(msg);
return 0;
}
const char *msgPtr = &msg[17];
if (strncmp(msgPtr, "START", 5) == 0)
{
const char *windowHandle = strstr(msgPtr, "EditorWindow") + 14;
editor_window_handle = (HWND)atoi(windowHandle);
}
else if (strncmp(msgPtr, "READY", 5) == 0)
{
free(msg);
return 2;
}
else if ((strncmp(msgPtr, "SETBREAK", 8) == 0) ||
(strncmp(msgPtr, "DELBREAK", 8) == 0))
{
bool isDelete = (msgPtr[0] == 'D');
// Format: SETBREAK $scriptname$lineNumber$
msgPtr += 10;
char scriptNameBuf[100];
int i = 0;
while (msgPtr[0] != '$')
{
scriptNameBuf[i] = msgPtr[0];
msgPtr++;
i++;
}
scriptNameBuf[i] = 0;
msgPtr++;
int lineNumber = atoi(msgPtr);
if (isDelete)
{
for (i = 0; i < numBreakpoints; i++)
{
if ((breakpoints[i].lineNumber == lineNumber) &&
(strcmp(breakpoints[i].scriptName, scriptNameBuf) == 0))
{
numBreakpoints--;
for (int j = i; j < numBreakpoints; j++)
{
breakpoints[j] = breakpoints[j + 1];
}
break;
}
}
}
else
{
strcpy(breakpoints[numBreakpoints].scriptName, scriptNameBuf);
breakpoints[numBreakpoints].lineNumber = lineNumber;
numBreakpoints++;
}
}
else if (strncmp(msgPtr, "RESUME", 6) == 0)
{
game_paused_in_debugger = 0;
}
else if (strncmp(msgPtr, "STEP", 4) == 0)
{
game_paused_in_debugger = 0;
break_on_next_script_step = 1;
}
else if (strncmp(msgPtr, "EXIT", 4) == 0)
{
want_exit = 1;
abort_engine = 1;
check_dynamic_sprites_at_exit = 0;
}
free(msg);
return 1;
}
return 0;
}
void NewInteractionCommand::remove () {
if (children != NULL) {
children->reset();
delete children;
}
children = NULL;
parent = NULL;
type = 0;
}
void force_audiostream_include() {
// This should never happen, but the call is here to make it
// link the audiostream libraries
stop_audio_stream(NULL);
}
AmbientSound ambient[MAX_SOUND_CHANNELS + 1]; // + 1 just for safety on array iterations
int get_volume_adjusted_for_distance(int volume, int sndX, int sndY, int sndMaxDist)
{
int distx = playerchar->x - sndX;
int disty = playerchar->y - sndY;
// it uses Allegro's "fix" sqrt without the ::
int dist = (int)::sqrt((double)(distx*distx + disty*disty));
// if they're quite close, full volume
int wantvol = volume;
if (dist >= AMBIENCE_FULL_DIST)
{
// get the relative volume
wantvol = ((dist - AMBIENCE_FULL_DIST) * volume) / sndMaxDist;
// closer is louder
wantvol = volume - wantvol;
}
return wantvol;
}
void update_directional_sound_vol()
{
for (int chan = 1; chan < MAX_SOUND_CHANNELS; chan++)
{
if ((channels[chan] != NULL) && (channels[chan]->done == 0) &&
(channels[chan]->xSource >= 0))
{
channels[chan]->directionalVolModifier =
get_volume_adjusted_for_distance(channels[chan]->vol,
channels[chan]->xSource,
channels[chan]->ySource,
channels[chan]->maximumPossibleDistanceAway) -
channels[chan]->vol;
channels[chan]->set_volume(channels[chan]->vol);
}
}
}
void update_ambient_sound_vol () {
for (int chan = 1; chan < MAX_SOUND_CHANNELS; chan++) {
AmbientSound *thisSound = &ambient[chan];
if (thisSound->channel == 0)
continue;
int sourceVolume = thisSound->vol;
if ((channels[SCHAN_SPEECH] != NULL) && (channels[SCHAN_SPEECH]->done == 0)) {
// Negative value means set exactly; positive means drop that amount
if (play.speech_music_drop < 0)
sourceVolume = -play.speech_music_drop;
else
sourceVolume -= play.speech_music_drop;
if (sourceVolume < 0)
sourceVolume = 0;
if (sourceVolume > 255)
sourceVolume = 255;
}
// Adjust ambient volume so it maxes out at overall sound volume
int ambientvol = (sourceVolume * play.sound_volume) / 255;
int wantvol;
if ((thisSound->x == 0) && (thisSound->y == 0)) {
wantvol = ambientvol;
}
else {
wantvol = get_volume_adjusted_for_distance(ambientvol, thisSound->x, thisSound->y, thisSound->maxdist);
}
if (channels[thisSound->channel] == NULL)
quit("Internal error: the ambient sound channel is enabled, but it has been destroyed");
channels[thisSound->channel]->set_volume(wantvol);
}
}
void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
if ((chid < 0) || (chid > MAX_SOUND_CHANNELS))
quit("!StopChannel: invalid channel ID");
if (channels[chid] != NULL) {
channels[chid]->destroy();
delete channels[chid];
channels[chid] = NULL;
}
if (play.crossfading_in_channel == chid)
play.crossfading_in_channel = 0;
if (play.crossfading_out_channel == chid)
play.crossfading_out_channel = 0;
// destroyed an ambient sound channel
if (ambient[chid].channel > 0)
ambient[chid].channel = 0;
if ((chid == SCHAN_MUSIC) && (resetLegacyMusicSettings))
{
play.cur_music_number = -1;
current_music_type = 0;
}
}
void stop_and_destroy_channel (int chid)
{
stop_and_destroy_channel_ex(chid, true);
}
void PlayMusicResetQueue(int newmus) {
play.music_queue_size = 0;
newmusic(newmus);
}
void StopAmbientSound (int channel) {
if ((channel < 0) || (channel >= MAX_SOUND_CHANNELS))
quit("!StopAmbientSound: invalid channel");
if (ambient[channel].channel == 0)
return;
stop_and_destroy_channel(channel);
ambient[channel].channel = 0;
}
SOUNDCLIP *load_sound_from_path(int soundNumber, int volume, bool repeat)
{
SOUNDCLIP *soundfx = load_sound_clip_from_old_style_number(false, soundNumber, repeat);
if (soundfx != NULL) {
if (soundfx->play() == 0)
soundfx = NULL;
}
return soundfx;
}
void PlayAmbientSound (int channel, int sndnum, int vol, int x, int y) {
// the channel parameter is to allow multiple ambient sounds in future
if ((channel < 1) || (channel == SCHAN_SPEECH) || (channel >= MAX_SOUND_CHANNELS))
quit("!PlayAmbientSound: invalid channel number");
if ((vol < 1) || (vol > 255))
quit("!PlayAmbientSound: volume must be 1 to 255");
if (usetup.digicard == DIGI_NONE)
return;
// only play the sound if it's not already playing
if ((ambient[channel].channel < 1) || (channels[ambient[channel].channel] == NULL) ||
(channels[ambient[channel].channel]->done == 1) ||
(ambient[channel].num != sndnum)) {
StopAmbientSound(channel);
// in case a normal non-ambient sound was playing, stop it too
stop_and_destroy_channel(channel);
SOUNDCLIP *asound = load_sound_from_path(sndnum, vol, true);
if (asound == NULL) {
debug_log ("Cannot load ambient sound %d", sndnum);
DEBUG_CONSOLE("FAILED to load ambient sound %d", sndnum);
return;
}
DEBUG_CONSOLE("Playing ambient sound %d on channel %d", sndnum, channel);
ambient[channel].channel = channel;
channels[channel] = asound;
channels[channel]->priority = 15; // ambient sound higher priority than normal sfx
}
// calculate the maximum distance away the player can be, using X
// only (since X centred is still more-or-less total Y)
ambient[channel].maxdist = ((x > thisroom.width / 2) ? x : (thisroom.width - x)) - AMBIENCE_FULL_DIST;
ambient[channel].num = sndnum;
ambient[channel].x = x;
ambient[channel].y = y;
ambient[channel].vol = vol;
update_ambient_sound_vol();
}
/*
#include "almp3_old.h"
ALLEGRO_MP3 *mp3ptr;
int mp3vol=128;
void amp_setvolume(int newvol) { mp3vol=newvol; }
int load_amp(char*namm,int loop) {
mp3ptr = new ALLEGRO_MP3(namm);
if (mp3ptr == NULL) return 0;
if (mp3ptr->get_error_code() != 0) {
delete mp3ptr;
return 0;
}
mp3ptr->play(mp3vol, 8192);
return 1;
}
void install_amp() { }
void unload_amp() {
mp3ptr->stop();
delete mp3ptr;
}
int amp_decode() {
mp3ptr->poll();
if (mp3ptr->is_finished()) {
if (play.music_repeat)
mp3ptr->play(mp3vol, 8192);
else return -1;
}
return 0;
}
*/
//#endif
// check and abort game if the script is currently
// inside the rep_exec_always function
void can_run_delayed_command() {
if (no_blocking_functions)
quit("!This command cannot be used within non-blocking events such as " REP_EXEC_ALWAYS_NAME);
}
const char *load_game_errors[9] =
{"No error","File not found","Not an AGS save game",
"Invalid save game version","Saved with different interpreter",
"Saved under a different game", "Resolution mismatch",
"Colour depth mismatch", ""};
void restart_game() {
can_run_delayed_command();
if (inside_script) {
curscript->queue_action(ePSARestartGame, 0, "RestartGame");
return;
}
int errcod;
if ((errcod = load_game(RESTART_POINT_SAVE_GAME_NUMBER, NULL, NULL))!=0)
quitprintf("unable to restart game (error:%s)", load_game_errors[-errcod]);
}
void setpal() {
wsetpalette(0,255,palette);
}
// Check that a supplied buffer from a text script function was not null
#define VALIDATE_STRING(strin) if ((unsigned long)strin <= 4096) quit("!String argument was null: make sure you pass a string, not an int, as a buffer")
// override packfile functions to allow it to load from our
// custom CLIB datafiles
extern "C" {
PACKFILE*_my_temppack;
extern char* clibgetdatafile(char*);
#if ALLEGRO_DATE > 19991010
#define PFO_PARAM const char *
#else
#define PFO_PARAM char *
#endif
#ifndef RTLD_NEXT
extern PACKFILE *__old_pack_fopen(PFO_PARAM,PFO_PARAM);
#endif
#if ALLEGRO_DATE > 19991010
PACKFILE *pack_fopen(const char *filnam1, const char *modd1) {
#else
PACKFILE *pack_fopen(char *filnam1, char *modd1) {
#endif
char *filnam = (char *)filnam1;
char *modd = (char *)modd1;
int needsetback = 0;
if (filnam[0] == '~') {
// ~ signals load from specific data file, not the main default one
char gfname[80];
int ii = 0;
filnam++;
while (filnam[0]!='~') {
gfname[ii] = filnam[0];
filnam++;
ii++;
}
filnam++;
// MACPORT FIX 9/6/5: changed from NULL TO '\0'
gfname[ii] = '\0';
/* char useloc[250];
#ifdef LINUX_VERSION
sprintf(useloc,"%s/%s",usetup.data_files_dir,gfname);
#else
sprintf(useloc,"%s\\%s",usetup.data_files_dir,gfname);
#endif
csetlib(useloc,"");*/
char *libname = ci_find_file(usetup.data_files_dir, gfname);
if (csetlib(libname,""))
{
// Hack for running in Debugger
free(libname);
libname = ci_find_file("Compiled", gfname);
csetlib(libname,"");
}
free(libname);
needsetback = 1;
}
// if the file exists, override the internal file
FILE *testf = fopen(filnam, "rb");
if (testf != NULL)
fclose(testf);
#ifdef RTLD_NEXT
static PACKFILE * (*__old_pack_fopen)(PFO_PARAM, PFO_PARAM) = NULL;
if(!__old_pack_fopen) {
__old_pack_fopen = (PACKFILE* (*)(PFO_PARAM, PFO_PARAM))dlsym(RTLD_NEXT, "pack_fopen");
if(!__old_pack_fopen) {
// Looks like we're linking statically to allegro...
// Let's see if it has been patched
__old_pack_fopen = (PACKFILE* (*)(PFO_PARAM, PFO_PARAM))dlsym(RTLD_DEFAULT, "__allegro_pack_fopen");
if(!__old_pack_fopen) {
fprintf(stderr, "If you're linking statically to allegro, you need to apply this patch to allegro:\n"
"https://sourceforge.net/tracker/?func=detail&aid=3302567&group_id=5665&atid=355665\n");
exit(1);
}
}
}
#endif
if ((cliboffset(filnam)<1) || (testf != NULL)) {
if (needsetback) csetlib(game_file_name,"");
return __old_pack_fopen(filnam, modd);
}
else {
_my_temppack=__old_pack_fopen(clibgetdatafile(filnam), modd);
if (_my_temppack == NULL)
quitprintf("pack_fopen: unable to change datafile: not found: %s", clibgetdatafile(filnam));
pack_fseek(_my_temppack,cliboffset(filnam));
#if ALLEGRO_DATE < 20050101
_my_temppack->todo=clibfilesize(filnam);
#else
_my_temppack->normal.todo = clibfilesize(filnam);
#endif
if (needsetback)
csetlib(game_file_name,"");
return _my_temppack;
}
}
} // end extern "C"
// end packfile functions
// Binary tree structure for holding translations, allows fast
// access
struct TreeMap {
TreeMap *left, *right;
char *text;
char *translation;
TreeMap() {
left = NULL;
right = NULL;
text = NULL;
translation = NULL;
}
char* findValue (const char* key) {
if (text == NULL)
return NULL;
if (strcmp(key, text) == 0)
return translation;
//debug_log("Compare: '%s' with '%s'", key, text);
if (strcmp (key, text) < 0) {
if (left == NULL)
return NULL;
return left->findValue (key);
}
else {
if (right == NULL)
return NULL;
return right->findValue (key);
}
}
void addText (const char* ntx, char *trans) {
if ((ntx == NULL) || (ntx[0] == 0) ||
((text != NULL) && (strcmp(ntx, text) == 0)))
// don't add if it's an empty string or if it's already here
return;
if (text == NULL) {
text = (char*)malloc(strlen(ntx)+1);
translation = (char*)malloc(strlen(trans)+1);
if (translation == NULL)
quit("load_translation: out of memory");
strcpy(text, ntx);
strcpy(translation, trans);
}
else if (strcmp(ntx, text) < 0) {
// Earlier in alphabet, add to left
if (left == NULL)
left = new TreeMap();
left->addText (ntx, trans);
}
else if (strcmp(ntx, text) > 0) {
// Later in alphabet, add to right
if (right == NULL)
right = new TreeMap();
right->addText (ntx, trans);
}
}
void clear() {
if (left) {
left->clear();
delete left;
}
if (right) {
right->clear();
delete right;
}
if (text)
free(text);
if (translation)
free(translation);
left = NULL;
right = NULL;
text = NULL;
translation = NULL;
}
~TreeMap() {
clear();
}
};
TreeMap *transtree = NULL;
long lang_offs_start = 0;
char transFileName[MAX_PATH] = "\0";
void close_translation () {
if (transtree != NULL) {
delete transtree;
transtree = NULL;
}
}
bool init_translation (const char *lang) {
char *transFileLoc;
if (lang == NULL) {
sprintf(transFileName, "default.tra");
}
else {
sprintf(transFileName, "%s.tra", lang);
}
transFileLoc = ci_find_file(usetup.data_files_dir, transFileName);
FILE *language_file = clibfopen(transFileLoc, "rb");
free(transFileLoc);
if (language_file == NULL)
{
if (lang != NULL)
{
// Just in case they're running in Debug, try compiled folder
sprintf(transFileName, "Compiled\\%s.tra", lang);
language_file = clibfopen(transFileName, "rb");
}
if (language_file == NULL)
return false;
}
// in case it's inside a library file, record the offset
lang_offs_start = ftell(language_file);
char transsig[16];
fread(transsig, 15, 1, language_file);
if (strcmp(transsig, "AGSTranslation") != 0) {
fclose(language_file);
return false;
}
if (transtree != NULL)
{
close_translation();
}
transtree = new TreeMap();
while (!feof (language_file)) {
int blockType = getw(language_file);
if (blockType == -1)
break;
// MACPORT FIX 9/6/5: remove warning
/* int blockSize = */ getw(language_file);
if (blockType == 1) {
char original[STD_BUFFER_SIZE], translation[STD_BUFFER_SIZE];
while (1) {
read_string_decrypt (language_file, original);
read_string_decrypt (language_file, translation);
if ((strlen (original) < 1) && (strlen(translation) < 1))
break;
if (feof (language_file))
quit("!Language file is corrupt");
transtree->addText (original, translation);
}
}
else if (blockType == 2) {
int uidfrom;
char wasgamename[100];
fread (&uidfrom, 4, 1, language_file);
read_string_decrypt (language_file, wasgamename);
if ((uidfrom != game.uniqueid) || (strcmp (wasgamename, game.gamename) != 0)) {
char quitmess[250];
sprintf(quitmess,
"!The translation file you have selected is not compatible with this game. "
"The translation is designed for '%s'. Make sure the translation was compiled by the original game author.",
wasgamename);
quit(quitmess);
}
}
else if (blockType == 3) {
// game settings
int temp = getw(language_file);
// normal font
if (temp >= 0)
SetNormalFont (temp);
temp = getw(language_file);
// speech font
if (temp >= 0)
SetSpeechFont (temp);
temp = getw(language_file);
// text direction
if (temp == 1) {
play.text_align = SCALIGN_LEFT;
game.options[OPT_RIGHTLEFTWRITE] = 0;
}
else if (temp == 2) {
play.text_align = SCALIGN_RIGHT;
game.options[OPT_RIGHTLEFTWRITE] = 1;
}
}
else
quit("Unknown block type in translation file.");
}
fclose (language_file);
if (transtree->text == NULL)
quit("!The selected translation file was empty. The translation source may have been translated incorrectly or you may have generated a blank file.");
return true;
}
char *get_translation (const char *text) {
if (text == NULL)
quit("!Null string supplied to CheckForTranslations");
source_text_length = strlen(text);
if ((text[0] == '&') && (play.unfactor_speech_from_textlength != 0)) {
// if there's an "&12 text" type line, remove "&12 " from the source
// length
int j = 0;
while ((text[j] != ' ') && (text[j] != 0))
j++;
j++;
source_text_length -= j;
}
// check if a plugin wants to translate it - if so, return that
char *plResult = (char*)platform->RunPluginHooks(AGSE_TRANSLATETEXT, (long)text);
if (plResult) {
// 64bit: This is a wonky way to detect a valid pointer
// if (((int)plResult >= -1) && ((int)plResult < 10000))
// quit("!Plugin did not return a string for text translation");
return plResult;
}
if (transtree != NULL) {
// translate the text using the translation file
char * transl = transtree->findValue (text);
if (transl != NULL)
return transl;
}
// return the original text
return (char*)text;
}
int IsTranslationAvailable () {
if (transtree != NULL)
return 1;
return 0;
}
int GetTranslationName (char* buffer) {
VALIDATE_STRING (buffer);
const char *copyFrom = transFileName;
while (strchr(copyFrom, '\\') != NULL)
{
copyFrom = strchr(copyFrom, '\\') + 1;
}
while (strchr(copyFrom, '/') != NULL)
{
copyFrom = strchr(copyFrom, '/') + 1;
}
strcpy (buffer, copyFrom);
// remove the ".tra" from the end of the filename
if (strstr (buffer, ".tra") != NULL)
strstr (buffer, ".tra")[0] = 0;
return IsTranslationAvailable();
}
const char* Game_GetTranslationFilename() {
char buffer[STD_BUFFER_SIZE];
GetTranslationName(buffer);
return CreateNewScriptString(buffer);
}
int Game_ChangeTranslation(const char *newFilename)
{
if ((newFilename == NULL) || (newFilename[0] == 0))
{
close_translation();
strcpy(transFileName, "");
return 1;
}
char oldTransFileName[MAX_PATH];
strcpy(oldTransFileName, transFileName);
if (!init_translation(newFilename))
{
strcpy(transFileName, oldTransFileName);
return 0;
}
return 1;
}
// End translation functions
volatile int timerloop=0;
volatile int mvolcounter = 0;
int update_music_at=0;
int time_between_timers=25; // in milliseconds
// our timer, used to keep game running at same speed on all systems
#if defined(WINDOWS_VERSION)
void __cdecl dj_timer_handler() {
#elif defined(LINUX_VERSION) || defined(MAC_VERSION)
extern "C" void dj_timer_handler() {
#else
void dj_timer_handler(...) {
#endif
timerloop++;
globalTimerCounter++;
if (mvolcounter > 0) mvolcounter++;
}
END_OF_FUNCTION(dj_timer_handler);
void set_game_speed(int fps) {
frames_per_second = fps;
time_between_timers = 1000 / fps;
install_int_ex(dj_timer_handler,MSEC_TO_TIMER(time_between_timers));
}
#ifdef USE_15BIT_FIX
extern "C" {
AL_FUNC(GFX_VTABLE *, _get_vtable, (int color_depth));
}
block convert_16_to_15(block iii) {
// int xx,yy,rpix;
int iwid = iii->w, ihit = iii->h;
int x,y;
if (bitmap_color_depth(iii) > 16) {
// we want a 32-to-24 conversion
block tempbl = create_bitmap_ex(final_col_dep,iwid,ihit);
if (final_col_dep < 24) {
// 32-to-16
blit(iii, tempbl, 0, 0, 0, 0, iwid, ihit);
return tempbl;
}
GFX_VTABLE *vtable = _get_vtable(final_col_dep);
if (vtable == NULL) {
quit("unable to get 24-bit bitmap vtable");
}
tempbl->vtable = vtable;
for (y=0; y < tempbl->h; y++) {
unsigned char*p32 = (unsigned char *)iii->line[y];
unsigned char*p24 = (unsigned char *)tempbl->line[y];
// strip out the alpha channel bit and copy the rest across
for (x=0; x < tempbl->w; x++) {
memcpy(&p24[x * 3], &p32[x * 4], 3);
}
}
return tempbl;
}
// we want a 16-to-15 converstion
unsigned short c,r,g,b;
// we do this process manually - no allegro color conversion
// because we store the RGB in a particular order in the data files
block tempbl = create_bitmap_ex(15,iwid,ihit);
GFX_VTABLE *vtable = _get_vtable(15);
if (vtable == NULL) {
quit("unable to get 15-bit bitmap vtable");
}
tempbl->vtable = vtable;
for (y=0; y < tempbl->h; y++) {
unsigned short*p16 = (unsigned short *)iii->line[y];
unsigned short*p15 = (unsigned short *)tempbl->line[y];
for (x=0; x < tempbl->w; x++) {
c = p16[x];
b = _rgb_scale_5[c & 0x1F];
g = _rgb_scale_6[(c >> 5) & 0x3F];
r = _rgb_scale_5[(c >> 11) & 0x1F];
p15[x] = makecol15(r, g, b);
}
}
/*
// the auto color conversion doesn't seem to work
for (xx=0;xx<iwid;xx++) {
for (yy=0;yy<ihit;yy++) {
rpix = _getpixel16(iii,xx,yy);
rpix = (rpix & 0x001f) | ((rpix >> 1) & 0x7fe0);
// again putpixel16 because the dest is actually 16bit
_putpixel15(tempbl,xx,yy,rpix);
}
}*/
return tempbl;
}
int _places_r = 3, _places_g = 2, _places_b = 3;
// convert RGB to BGR for strange graphics cards
block convert_16_to_16bgr(block tempbl) {
int x,y;
unsigned short c,r,g,b;
for (y=0; y < tempbl->h; y++) {
unsigned short*p16 = (unsigned short *)tempbl->line[y];
for (x=0; x < tempbl->w; x++) {
c = p16[x];
if (c != MASK_COLOR_16) {
b = _rgb_scale_5[c & 0x1F];
g = _rgb_scale_6[(c >> 5) & 0x3F];
r = _rgb_scale_5[(c >> 11) & 0x1F];
// allegro assumes 5-6-5 for 16-bit
p16[x] = (((r >> _places_r) << _rgb_r_shift_16) |
((g >> _places_g) << _rgb_g_shift_16) |
((b >> _places_b) << _rgb_b_shift_16));
}
}
}
return tempbl;
}
#endif
// PSP: convert 32 bit RGB to BGR.
block convert_32_to_32bgr(block tempbl) {
unsigned char* current = tempbl->line[0];
int i = 0;
int j = 0;
while (i < tempbl->h)
{
current = tempbl->line[i];
while (j < tempbl->w)
{
current[0] ^= current[2];
current[2] ^= current[0];
current[0] ^= current[2];
current += 4;
j++;
}
i++;
j = 0;
}
return tempbl;
}
// Begin resolution system functions
// Multiplies up the number of pixels depending on the current
// resolution, to give a relatively fixed size at any game res
AGS_INLINE int get_fixed_pixel_size(int pixels)
{
return pixels * current_screen_resolution_multiplier;
}
AGS_INLINE int convert_to_low_res(int coord)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
return coord;
else
return coord / current_screen_resolution_multiplier;
}
AGS_INLINE int convert_back_to_high_res(int coord)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
return coord;
else
return coord * current_screen_resolution_multiplier;
}
AGS_INLINE int multiply_up_coordinate(int coord)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
return coord * current_screen_resolution_multiplier;
else
return coord;
}
AGS_INLINE void multiply_up_coordinates(int *x, int *y)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
{
x[0] *= current_screen_resolution_multiplier;
y[0] *= current_screen_resolution_multiplier;
}
}
AGS_INLINE void multiply_up_coordinates_round_up(int *x, int *y)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
{
x[0] = x[0] * current_screen_resolution_multiplier + (current_screen_resolution_multiplier - 1);
y[0] = y[0] * current_screen_resolution_multiplier + (current_screen_resolution_multiplier - 1);
}
}
AGS_INLINE int divide_down_coordinate(int coord)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
return coord / current_screen_resolution_multiplier;
else
return coord;
}
AGS_INLINE int divide_down_coordinate_round_up(int coord)
{
if (game.options[OPT_NATIVECOORDINATES] == 0)
return (coord / current_screen_resolution_multiplier) + (current_screen_resolution_multiplier - 1);
else
return coord;
}
// End resolution system functions
int wgetfontheight(int font) {
int htof = wgettextheight(heightTestString, font);
// automatic outline fonts are 2 pixels taller
if (game.fontoutline[font] == FONT_OUTLINE_AUTO) {
// scaled up SCI font, push outline further out
if ((game.options[OPT_NOSCALEFNT] == 0) && (!fontRenderers[font]->SupportsExtendedCharacters(font)))
htof += get_fixed_pixel_size(2);
// otherwise, just push outline by 1 pixel
else
htof += 2;
}
return htof;
}
int wgettextwidth_compensate(const char *tex, int font) {
int wdof = wgettextwidth(tex, font);
if (game.fontoutline[font] == FONT_OUTLINE_AUTO) {
// scaled up SCI font, push outline further out
if ((game.options[OPT_NOSCALEFNT] == 0) && (!fontRenderers[font]->SupportsExtendedCharacters(font)))
wdof += get_fixed_pixel_size(2);
// otherwise, just push outline by 1 pixel
else
wdof += get_fixed_pixel_size(1);
}
return wdof;
}
// ** dirty rectangle system **
#define MAXDIRTYREGIONS 25
#define WHOLESCREENDIRTY (MAXDIRTYREGIONS + 5)
#define MAX_SPANS_PER_ROW 4
struct InvalidRect {
int x1, y1, x2, y2;
};
struct IRSpan {
int x1, x2;
int mergeSpan(int tx1, int tx2);
};
struct IRRow {
IRSpan span[MAX_SPANS_PER_ROW];
int numSpans;
};
IRRow *dirtyRow = NULL;
int _dirtyRowSize;
InvalidRect dirtyRegions[MAXDIRTYREGIONS];
int numDirtyRegions = 0;
int numDirtyBytes = 0;
int IRSpan::mergeSpan(int tx1, int tx2) {
if ((tx1 > x2) || (tx2 < x1))
return 0;
// overlapping, increase the span
if (tx1 < x1)
x1 = tx1;
if (tx2 > x2)
x2 = tx2;
return 1;
}
void init_invalid_regions(int scrnHit) {
numDirtyRegions = WHOLESCREENDIRTY;
dirtyRow = (IRRow*)malloc(sizeof(IRRow) * scrnHit);
memset(dirtyRow, 0, sizeof(IRRow) * scrnHit);
for (int e = 0; e < scrnHit; e++)
dirtyRow[e].numSpans = 0;
_dirtyRowSize = scrnHit;
}
void update_invalid_region(int x, int y, block src, block dest) {
int i;
// convert the offsets for the destination into
// offsets into the source
x = -x;
y = -y;
if (numDirtyRegions == WHOLESCREENDIRTY) {
blit(src, dest, x, y, 0, 0, dest->w, dest->h);
}
else {
int k, tx1, tx2, srcdepth = bitmap_color_depth(src);
if ((srcdepth == bitmap_color_depth(dest)) && (is_memory_bitmap(dest))) {
int bypp = bmp_bpp(src);
// do the fast copy
for (i = 0; i < scrnhit; i++) {
for (k = 0; k < dirtyRow[i].numSpans; k++) {
tx1 = dirtyRow[i].span[k].x1;
tx2 = dirtyRow[i].span[k].x2;
memcpyfast(&dest->line[i][tx1 * bypp], &src->line[i + y][(tx1 + x) * bypp], ((tx2 - tx1) + 1) * bypp);
}
}
}
else {
// do the fast copy
int rowsInOne;
for (i = 0; i < scrnhit; i++) {
rowsInOne = 1;
// if there are rows with identical masks, do them all in one go
while ((i+rowsInOne < scrnhit) && (memcmp(&dirtyRow[i], &dirtyRow[i+rowsInOne], sizeof(IRRow)) == 0))
rowsInOne++;
for (k = 0; k < dirtyRow[i].numSpans; k++) {
tx1 = dirtyRow[i].span[k].x1;
tx2 = dirtyRow[i].span[k].x2;
blit(src, dest, tx1 + x, i + y, tx1, i, (tx2 - tx1) + 1, rowsInOne);
}
i += (rowsInOne - 1);
}
}
/* else {
// update the dirty regions
for (i = 0; i < numDirtyRegions; i++) {
blit(src, dest, x + dirtyRegions[i].x1, y + dirtyRegions[i].y1,
dirtyRegions[i].x1, dirtyRegions[i].y1,
(dirtyRegions[i].x2 - dirtyRegions[i].x1) + 1,
(dirtyRegions[i].y2 - dirtyRegions[i].y1) + 1);
}
}*/
}
}
void update_invalid_region_and_reset(int x, int y, block src, block dest) {
int i;
update_invalid_region(x, y, src, dest);
// screen has been updated, no longer dirty
numDirtyRegions = 0;
numDirtyBytes = 0;
for (i = 0; i < _dirtyRowSize; i++)
dirtyRow[i].numSpans = 0;
}
int combine_new_rect(InvalidRect *r1, InvalidRect *r2) {
// check if new rect is within old rect X-wise
if ((r2->x1 >= r1->x1) && (r2->x2 <= r1->x2)) {
if ((r2->y1 >= r1->y1) && (r2->y2 <= r1->y2)) {
// Y is also within the old one - scrap the new rect
return 1;
}
}
return 0;
}
void invalidate_rect(int x1, int y1, int x2, int y2) {
if (numDirtyRegions >= MAXDIRTYREGIONS) {
// too many invalid rectangles, just mark the whole thing dirty
numDirtyRegions = WHOLESCREENDIRTY;
return;
}
int a;
if (x1 >= scrnwid) x1 = scrnwid-1;
if (y1 >= scrnhit) y1 = scrnhit-1;
if (x2 >= scrnwid) x2 = scrnwid-1;
if (y2 >= scrnhit) y2 = scrnhit-1;
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 < 0) x2 = 0;
if (y2 < 0) y2 = 0;
/*
dirtyRegions[numDirtyRegions].x1 = x1;
dirtyRegions[numDirtyRegions].y1 = y1;
dirtyRegions[numDirtyRegions].x2 = x2;
dirtyRegions[numDirtyRegions].y2 = y2;
for (a = 0; a < numDirtyRegions; a++) {
// see if we can merge it into any other regions
if (combine_new_rect(&dirtyRegions[a], &dirtyRegions[numDirtyRegions]))
return;
}
numDirtyBytes += (x2 - x1) * (y2 - y1);
if (numDirtyBytes > (scrnwid * scrnhit) / 2)
numDirtyRegions = WHOLESCREENDIRTY;
else {*/
numDirtyRegions++;
// ** Span code
int s, foundOne;
// add this rect to the list for this row
for (a = y1; a <= y2; a++) {
foundOne = 0;
for (s = 0; s < dirtyRow[a].numSpans; s++) {
if (dirtyRow[a].span[s].mergeSpan(x1, x2)) {
foundOne = 1;
break;
}
}
if (foundOne) {
// we were merged into a span, so we're ok
int t;
// check whether now two of the spans overlap each other
// in which case merge them
for (s = 0; s < dirtyRow[a].numSpans; s++) {
for (t = s + 1; t < dirtyRow[a].numSpans; t++) {
if (dirtyRow[a].span[s].mergeSpan(dirtyRow[a].span[t].x1, dirtyRow[a].span[t].x2)) {
dirtyRow[a].numSpans--;
for (int u = t; u < dirtyRow[a].numSpans; u++)
dirtyRow[a].span[u] = dirtyRow[a].span[u + 1];
break;
}
}
}
}
else if (dirtyRow[a].numSpans < MAX_SPANS_PER_ROW) {
dirtyRow[a].span[dirtyRow[a].numSpans].x1 = x1;
dirtyRow[a].span[dirtyRow[a].numSpans].x2 = x2;
dirtyRow[a].numSpans++;
}
else {
// didn't fit in an existing span, and there are none spare
int nearestDist = 99999, nearestWas = -1, extendLeft;
int tleft, tright;
// find the nearest span, and enlarge that to include this rect
for (s = 0; s < dirtyRow[a].numSpans; s++) {
tleft = dirtyRow[a].span[s].x1 - x2;
if ((tleft > 0) && (tleft < nearestDist)) {
nearestDist = tleft;
nearestWas = s;
extendLeft = 1;
}
tright = x1 - dirtyRow[a].span[s].x2;
if ((tright > 0) && (tright < nearestDist)) {
nearestDist = tright;
nearestWas = s;
extendLeft = 0;
}
}
if (extendLeft)
dirtyRow[a].span[nearestWas].x1 = x1;
else
dirtyRow[a].span[nearestWas].x2 = x2;
}
}
// ** End span code
//}
}
void invalidate_sprite(int x1, int y1, IDriverDependantBitmap *pic) {
invalidate_rect(x1, y1, x1 + pic->GetWidth(), y1 + pic->GetHeight());
}
void draw_and_invalidate_text(int x1, int y1, int font, const char *text) {
wouttext_outline(x1, y1, font, (char*)text);
invalidate_rect(x1, y1, x1 + wgettextwidth_compensate(text, font), y1 + wgetfontheight(font) + get_fixed_pixel_size(1));
}
void invalidate_screen() {
// mark the whole screen dirty
numDirtyRegions = WHOLESCREENDIRTY;
}
// ** dirty rectangle system end **
void mark_current_background_dirty()
{
current_background_is_dirty = true;
}
inline int is_valid_object(int obtest) {
if ((obtest < 0) || (obtest >= croom->numobj)) return 0;
return 1;
}
void SetAmbientTint (int red, int green, int blue, int opacity, int luminance) {
if ((red < 0) || (green < 0) || (blue < 0) ||
(red > 255) || (green > 255) || (blue > 255) ||
(opacity < 0) || (opacity > 100) ||
(luminance < 0) || (luminance > 100))
quit("!SetTint: invalid parameter. R,G,B must be 0-255, opacity & luminance 0-100");
DEBUG_CONSOLE("Set ambient tint RGB(%d,%d,%d) %d%%", red, green, blue, opacity);
play.rtint_red = red;
play.rtint_green = green;
play.rtint_blue = blue;
play.rtint_level = opacity;
play.rtint_light = (luminance * 25) / 10;
}
void SetObjectTint(int obj, int red, int green, int blue, int opacity, int luminance) {
if ((red < 0) || (green < 0) || (blue < 0) ||
(red > 255) || (green > 255) || (blue > 255) ||
(opacity < 0) || (opacity > 100) ||
(luminance < 0) || (luminance > 100))
quit("!SetObjectTint: invalid parameter. R,G,B must be 0-255, opacity & luminance 0-100");
if (!is_valid_object(obj))
quit("!SetObjectTint: invalid object number specified");
DEBUG_CONSOLE("Set object %d tint RGB(%d,%d,%d) %d%%", obj, red, green, blue, opacity);
objs[obj].tint_r = red;
objs[obj].tint_g = green;
objs[obj].tint_b = blue;
objs[obj].tint_level = opacity;
objs[obj].tint_light = (luminance * 25) / 10;
objs[obj].flags |= OBJF_HASTINT;
}
void Object_Tint(ScriptObject *objj, int red, int green, int blue, int saturation, int luminance) {
SetObjectTint(objj->id, red, green, blue, saturation, luminance);
}
void RemoveObjectTint(int obj) {
if (!is_valid_object(obj))
quit("!RemoveObjectTint: invalid object");
if (objs[obj].flags & OBJF_HASTINT) {
DEBUG_CONSOLE("Un-tint object %d", obj);
objs[obj].flags &= ~OBJF_HASTINT;
}
else {
debug_log("RemoveObjectTint called but object was not tinted");
}
}
void Object_RemoveTint(ScriptObject *objj) {
RemoveObjectTint(objj->id);
}
void TintScreen(int red, int grn, int blu) {
if ((red < 0) || (grn < 0) || (blu < 0) || (red > 100) || (grn > 100) || (blu > 100))
quit("!TintScreen: RGB values must be 0-100");
invalidate_screen();
if ((red == 0) && (grn == 0) && (blu == 0)) {
play.screen_tint = -1;
return;
}
red = (red * 25) / 10;
grn = (grn * 25) / 10;
blu = (blu * 25) / 10;
play.screen_tint = red + (grn << 8) + (blu << 16);
}
int get_screen_y_adjustment(BITMAP *checkFor) {
if ((screen == _sub_screen) && (checkFor->h < final_scrn_hit))
return get_fixed_pixel_size(20);
return 0;
}
int get_screen_x_adjustment(BITMAP *checkFor) {
if ((screen == _sub_screen) && (checkFor->w < final_scrn_wid))
return (final_scrn_wid - checkFor->w) / 2;
return 0;
}
void render_black_borders(int atx, int aty)
{
if (!gfxDriver->UsesMemoryBackBuffer())
{
if (aty > 0)
{
// letterbox borders
blankImage->SetStretch(scrnwid, aty);
gfxDriver->DrawSprite(0, -aty, blankImage);
gfxDriver->DrawSprite(0, scrnhit, blankImage);
}
if (atx > 0)
{
// sidebar borders for widescreen
blankSidebarImage->SetStretch(atx, scrnhit);
gfxDriver->DrawSprite(-atx, 0, blankSidebarImage);
gfxDriver->DrawSprite(scrnwid, 0, blankSidebarImage);
}
}
}
void render_to_screen(BITMAP *toRender, int atx, int aty) {
atx += get_screen_x_adjustment(toRender);
aty += get_screen_y_adjustment(toRender);
gfxDriver->SetRenderOffset(atx, aty);
render_black_borders(atx, aty);
gfxDriver->DrawSprite(AGSE_FINALSCREENDRAW, 0, NULL);
if (play.screen_is_faded_out)
{
if (gfxDriver->UsesMemoryBackBuffer())
gfxDriver->RenderToBackBuffer();
gfxDriver->ClearDrawList();
return;
}
// only vsync in full screen mode, it makes things worse
// in a window
gfxDriver->EnableVsyncBeforeRender((scsystem.vsync > 0) && (usetup.windowed == 0));
bool succeeded = false;
while (!succeeded)
{
try
{
gfxDriver->Render((GlobalFlipType)play.screen_flipped);
#if defined(ANDROID_VERSION)
if (game.color_depth == 1)
android_render();
#elif defined(IOS_VERSION)
if (game.color_depth == 1)
ios_render();
#endif
succeeded = true;
}
catch (Ali3DFullscreenLostException)
{
platform->Delay(500);
}
}
}
void clear_letterbox_borders() {
if (multiply_up_coordinate(thisroom.height) < final_scrn_hit) {
// blank out any traces in borders left by a full-screen room
gfxDriver->ClearRectangle(0, 0, _old_screen->w - 1, get_fixed_pixel_size(20) - 1, NULL);
gfxDriver->ClearRectangle(0, final_scrn_hit - get_fixed_pixel_size(20), _old_screen->w - 1, final_scrn_hit - 1, NULL);
}
}
// writes the virtual screen to the screen, converting colours if
// necessary
void write_screen() {
if (play.fast_forward)
return;
static int wasShakingScreen = 0;
bool clearScreenBorders = false;
int at_yp = 0;
if (play.shakesc_length > 0) {
wasShakingScreen = 1;
if ( (loopcounter % play.shakesc_delay) < (play.shakesc_delay / 2) )
at_yp = multiply_up_coordinate(play.shakesc_amount);
invalidate_screen();
}
else if (wasShakingScreen) {
wasShakingScreen = 0;
if (!gfxDriver->RequiresFullRedrawEachFrame())
{
clear_letterbox_borders();
}
}
if (play.screen_tint < 1)
gfxDriver->SetScreenTint(0, 0, 0);
else
gfxDriver->SetScreenTint(play.screen_tint & 0xff, (play.screen_tint >> 8) & 0xff, (play.screen_tint >> 16) & 0xff);
render_to_screen(virtual_screen, 0, at_yp);
}
extern char buffer2[60];
int oldmouse;
void setup_for_dialog() {
cbuttfont = play.normal_font;
acdialog_font = play.normal_font;
wsetscreen(virtual_screen);
if (!play.mouse_cursor_hidden)
domouse(1);
oldmouse=cur_cursor; set_mouse_cursor(CURS_ARROW);
}
void restore_after_dialog() {
set_mouse_cursor(oldmouse);
if (!play.mouse_cursor_hidden)
domouse(2);
construct_virtual_screen(true);
}
void RestoreGameSlot(int slnum) {
if (displayed_room < 0)
quit("!RestoreGameSlot: a game cannot be restored from within game_start");
can_run_delayed_command();
if (inside_script) {
curscript->queue_action(ePSARestoreGame, slnum, "RestoreGameSlot");
return;
}
load_game(slnum, NULL, NULL);
}
void get_save_game_path(int slotNum, char *buffer) {
strcpy(buffer, saveGameDirectory);
sprintf(&buffer[strlen(buffer)], sgnametemplate, slotNum);
strcat(buffer, saveGameSuffix);
}
void DeleteSaveSlot (int slnum) {
char nametouse[260];
get_save_game_path(slnum, nametouse);
unlink (nametouse);
if ((slnum >= 1) && (slnum <= MAXSAVEGAMES)) {
char thisname[260];
for (int i = MAXSAVEGAMES; i > slnum; i--) {
get_save_game_path(i, thisname);
FILE *fin = fopen (thisname, "rb");
if (fin != NULL) {
fclose (fin);
// Rename the highest save game to fill in the gap
rename (thisname, nametouse);
break;
}
}
}
}
int Game_SetSaveGameDirectory(const char *newFolder) {
// don't allow them to go to another folder
if ((newFolder[0] == '/') || (newFolder[0] == '\\') ||
(newFolder[0] == ' ') ||
((newFolder[0] != 0) && (newFolder[1] == ':')))
return 0;
char newSaveGameDir[260];
platform->ReplaceSpecialPaths(newFolder, newSaveGameDir);
fix_filename_slashes(newSaveGameDir);
#if defined(LINUX_VERSION) || defined(MAC_VERSION)
mkdir(newSaveGameDir, 0);
#else
mkdir(newSaveGameDir);
#endif
put_backslash(newSaveGameDir);
char newFolderTempFile[260];
strcpy(newFolderTempFile, newSaveGameDir);
strcat(newFolderTempFile, "agstmp.tmp");
FILE *testTemp = fopen(newFolderTempFile, "wb");
if (testTemp == NULL) {
return 0;
}
fclose(testTemp);
unlink(newFolderTempFile);
// copy the Restart Game file, if applicable
char restartGamePath[260];
sprintf(restartGamePath, "%s""agssave.%d%s", saveGameDirectory, RESTART_POINT_SAVE_GAME_NUMBER, saveGameSuffix);
FILE *restartGameFile = fopen(restartGamePath, "rb");
if (restartGameFile != NULL) {
long fileSize = filelength(fileno(restartGameFile));
char *mbuffer = (char*)malloc(fileSize);
fread(mbuffer, fileSize, 1, restartGameFile);
fclose(restartGameFile);
sprintf(restartGamePath, "%s""agssave.%d%s", newSaveGameDir, RESTART_POINT_SAVE_GAME_NUMBER, saveGameSuffix);
restartGameFile = fopen(restartGamePath, "wb");
fwrite(mbuffer, fileSize, 1, restartGameFile);
fclose(restartGameFile);
free(mbuffer);
}
strcpy(saveGameDirectory, newSaveGameDir);
return 1;
}
int GetSaveSlotDescription(int slnum,char*desbuf) {
VALIDATE_STRING(desbuf);
if (load_game(slnum, desbuf, NULL) == 0)
return 1;
sprintf(desbuf,"INVALID SLOT %d", slnum);
return 0;
}
const char* Game_GetSaveSlotDescription(int slnum) {
char buffer[STD_BUFFER_SIZE];
if (load_game(slnum, buffer, NULL) == 0)
return CreateNewScriptString(buffer);
return NULL;
}
int LoadSaveSlotScreenshot(int slnum, int width, int height) {
int gotSlot;
multiply_up_coordinates(&width, &height);
if (load_game(slnum, NULL, &gotSlot) != 0)
return 0;
if (gotSlot == 0)
return 0;
if ((spritewidth[gotSlot] == width) && (spriteheight[gotSlot] == height))
return gotSlot;
// resize the sprite to the requested size
block newPic = create_bitmap_ex(bitmap_color_depth(spriteset[gotSlot]), width, height);
stretch_blit(spriteset[gotSlot], newPic,
0, 0, spritewidth[gotSlot], spriteheight[gotSlot],
0, 0, width, height);
update_polled_stuff();
// replace the bitmap in the sprite set
free_dynamic_sprite(gotSlot);
add_dynamic_sprite(gotSlot, newPic);
return gotSlot;
}
void get_current_dir_path(char* buffer, const char *fileName)
{
if (use_compiled_folder_as_current_dir)
{
sprintf(buffer, "Compiled\\%s", fileName);
}
else
{
strcpy(buffer, fileName);
}
}
int LoadImageFile(const char *filename) {
char loadFromPath[MAX_PATH];
get_current_dir_path(loadFromPath, filename);
block loadedFile = load_bitmap(loadFromPath, NULL);
if (loadedFile == NULL)
return 0;
int gotSlot = spriteset.findFreeSlot();
if (gotSlot <= 0)
return 0;
add_dynamic_sprite(gotSlot, gfxDriver->ConvertBitmapToSupportedColourDepth(loadedFile));
return gotSlot;
}
int load_game_and_print_error(int toload) {
int ecret = load_game(toload, NULL, NULL);
if (ecret < 0) {
// disable speech in case there are dynamic graphics that
// have been freed
int oldalways = game.options[OPT_ALWAYSSPCH];
game.options[OPT_ALWAYSSPCH] = 0;
Display("Unable to load game (error: %s).",load_game_errors[-ecret]);
game.options[OPT_ALWAYSSPCH] = oldalways;
}
return ecret;
}
void restore_game_dialog() {
can_run_delayed_command();
if (thisroom.options[ST_SAVELOAD] == 1) {
DisplayMessage (983);
return;
}
if (inside_script) {
curscript->queue_action(ePSARestoreGameDialog, 0, "RestoreGameDialog");
return;
}
setup_for_dialog();
int toload=loadgamedialog();
restore_after_dialog();
if (toload>=0) {
load_game_and_print_error(toload);
}
}
void save_game_dialog() {
if (thisroom.options[ST_SAVELOAD] == 1) {
DisplayMessage (983);
return;
}
if (inside_script) {
curscript->queue_action(ePSASaveGameDialog, 0, "SaveGameDialog");
return;
}
setup_for_dialog();
int toload=savegamedialog();
restore_after_dialog();
if (toload>=0)
save_game(toload,buffer2);
}
void update_script_mouse_coords() {
scmouse.x = divide_down_coordinate(mousex);
scmouse.y = divide_down_coordinate(mousey);
}
char scfunctionname[30];
int prepare_text_script(ccInstance*sci,char**tsname) {
ccError=0;
if (sci==NULL) return -1;
if (ccGetSymbolAddr(sci,tsname[0]) == NULL) {
strcpy (ccErrorString, "no such function in script");
return -2;
}
if (sci->pc!=0) {
strcpy(ccErrorString,"script is already in execution");
return -3;
}
scripts[num_scripts].init();
scripts[num_scripts].inst = sci;
/* char tempb[300];
sprintf(tempb,"Creating script instance for '%s' room %d",tsname[0],displayed_room);
write_log(tempb);*/
if (sci->pc != 0) {
// write_log("Forking instance");
scripts[num_scripts].inst = ccForkInstance(sci);
if (scripts[num_scripts].inst == NULL)
quit("unable to fork instance for secondary script");
scripts[num_scripts].forked = 1;
}
curscript = &scripts[num_scripts];
num_scripts++;
if (num_scripts >= MAX_SCRIPT_AT_ONCE)
quit("too many nested text script instances created");
// in case script_run_another is the function name, take a backup
strcpy(scfunctionname,tsname[0]);
tsname[0]=&scfunctionname[0];
update_script_mouse_coords();
inside_script++;
// aborted_ip=0;
// abort_executor=0;
return 0;
}
void cancel_all_scripts() {
int aa;
for (aa = 0; aa < num_scripts; aa++) {
if (scripts[aa].forked)
ccAbortAndDestroyInstance(scripts[aa].inst);
else
ccAbortInstance(scripts[aa].inst);
scripts[aa].numanother = 0;
}
num_scripts = 0;
/* if (gameinst!=NULL) ccAbortInstance(gameinst);
if (roominst!=NULL) ccAbortInstance(roominst);*/
}
void post_script_cleanup() {
// should do any post-script stuff here, like go to new room
if (ccError) quit(ccErrorString);
ExecutingScript copyof = scripts[num_scripts-1];
// write_log("Instance finished.");
if (scripts[num_scripts-1].forked)
ccFreeInstance(scripts[num_scripts-1].inst);
num_scripts--;
inside_script--;
if (num_scripts > 0)
curscript = &scripts[num_scripts-1];
else {
curscript = NULL;
}
// if (abort_executor) user_disabled_data2=aborted_ip;
int old_room_number = displayed_room;
// run the queued post-script actions
for (int ii = 0; ii < copyof.numPostScriptActions; ii++) {
int thisData = copyof.postScriptActionData[ii];
switch (copyof.postScriptActions[ii]) {
case ePSANewRoom:
// only change rooms when all scripts are done
if (num_scripts == 0) {
new_room(thisData, playerchar);
// don't allow any pending room scripts from the old room
// in run_another to be executed
return;
}
else
curscript->queue_action(ePSANewRoom, thisData, "NewRoom");
break;
case ePSAInvScreen:
invscreen();
break;
case ePSARestoreGame:
cancel_all_scripts();
load_game_and_print_error(thisData);
return;
case ePSARestoreGameDialog:
restore_game_dialog();
return;
case ePSARunAGSGame:
cancel_all_scripts();
load_new_game = thisData;
return;
case ePSARunDialog:
do_conversation(thisData);
break;
case ePSARestartGame:
cancel_all_scripts();
restart_game();
return;
case ePSASaveGame:
save_game(thisData, copyof.postScriptSaveSlotDescription[ii]);
break;
case ePSASaveGameDialog:
save_game_dialog();
break;
default:
quitprintf("undefined post script action found: %d", copyof.postScriptActions[ii]);
}
// if the room changed in a conversation, for example, abort
if (old_room_number != displayed_room) {
return;
}
}
int jj;
for (jj = 0; jj < copyof.numanother; jj++) {
old_room_number = displayed_room;
char runnext[40];
strcpy(runnext,copyof.script_run_another[jj]);
copyof.script_run_another[jj][0]=0;
if (runnext[0]=='#')
run_text_script_2iparam(gameinst,&runnext[1],copyof.run_another_p1[jj],copyof.run_another_p2[jj]);
else if (runnext[0]=='!')
run_text_script_iparam(gameinst,&runnext[1],copyof.run_another_p1[jj]);
else if (runnext[0]=='|')
run_text_script(roominst,&runnext[1]);
else if (runnext[0]=='%')
run_text_script_2iparam(roominst, &runnext[1], copyof.run_another_p1[jj], copyof.run_another_p2[jj]);
else if (runnext[0]=='$') {
run_text_script_iparam(roominst,&runnext[1],copyof.run_another_p1[jj]);
play.roomscript_finished = 1;
}
else
run_text_script(gameinst,runnext);
// if they've changed rooms, cancel any further pending scripts
if ((displayed_room != old_room_number) || (load_new_game))
break;
}
copyof.numanother = 0;
}
void quit_with_script_error(const char *functionName)
{
quitprintf("%sError running function '%s':\n%s", (ccErrorIsUserError ? "!" : ""), functionName, ccErrorString);
}
void _do_run_script_func_cant_block(ccInstance *forkedinst, NonBlockingScriptFunction* funcToRun, bool *hasTheFunc) {
if (!hasTheFunc[0])
return;
no_blocking_functions++;
int result;
if (funcToRun->numParameters == 0)
result = ccCallInstance(forkedinst, (char*)funcToRun->functionName, 0);
else if (funcToRun->numParameters == 1)
result = ccCallInstance(forkedinst, (char*)funcToRun->functionName, 1, funcToRun->param1);
else if (funcToRun->numParameters == 2)
result = ccCallInstance(forkedinst, (char*)funcToRun->functionName, 2, funcToRun->param1, funcToRun->param2);
else
quit("_do_run_script_func_cant_block called with too many parameters");
if (result == -2) {
// the function doens't exist, so don't try and run it again
hasTheFunc[0] = false;
}
else if ((result != 0) && (result != 100)) {
quit_with_script_error(funcToRun->functionName);
}
else
{
funcToRun->atLeastOneImplementationExists = true;
}
// this might be nested, so don't disrupt blocked scripts
ccErrorString[0] = 0;
ccError = 0;
no_blocking_functions--;
}
void run_function_on_non_blocking_thread(NonBlockingScriptFunction* funcToRun) {
update_script_mouse_coords();
int room_changes_was = play.room_changes;
funcToRun->atLeastOneImplementationExists = false;
// run modules
// modules need a forkedinst for this to work
for (int kk = 0; kk < numScriptModules; kk++) {
_do_run_script_func_cant_block(moduleInstFork[kk], funcToRun, &funcToRun->moduleHasFunction[kk]);
if (room_changes_was != play.room_changes)
return;
}
_do_run_script_func_cant_block(gameinstFork, funcToRun, &funcToRun->globalScriptHasFunction);
if (room_changes_was != play.room_changes)
return;
_do_run_script_func_cant_block(roominstFork, funcToRun, &funcToRun->roomHasFunction);
}
int run_script_function_if_exist(ccInstance*sci,char*tsname,int numParam, long iparam, long iparam2, long iparam3) {
int oldRestoreCount = gameHasBeenRestored;
// First, save the current ccError state
// This is necessary because we might be attempting
// to run Script B, while Script A is still running in the
// background.
// If CallInstance here has an error, it would otherwise
// also abort Script A because ccError is a global variable.
int cachedCcError = ccError;
ccError = 0;
int toret = prepare_text_script(sci,&tsname);
if (toret) {
ccError = cachedCcError;
return -18;
}
// Clear the error message
ccErrorString[0] = 0;
if (numParam == 0)
toret = ccCallInstance(curscript->inst,tsname,numParam);
else if (numParam == 1)
toret = ccCallInstance(curscript->inst,tsname,numParam, iparam);
else if (numParam == 2)
toret = ccCallInstance(curscript->inst,tsname,numParam,iparam, iparam2);
else if (numParam == 3)
toret = ccCallInstance(curscript->inst,tsname,numParam,iparam, iparam2, iparam3);
else
quit("Too many parameters to run_script_function_if_exist");
// 100 is if Aborted (eg. because we are LoadAGSGame'ing)
if ((toret != 0) && (toret != -2) && (toret != 100)) {
quit_with_script_error(tsname);
}
post_script_cleanup_stack++;
if (post_script_cleanup_stack > 50)
quitprintf("!post_script_cleanup call stack exceeded: possible recursive function call? running %s", tsname);
post_script_cleanup();
post_script_cleanup_stack--;
// restore cached error state
ccError = cachedCcError;
// if the game has been restored, ensure that any further scripts are not run
if ((oldRestoreCount != gameHasBeenRestored) && (eventClaimed == EVENT_INPROGRESS))
eventClaimed = EVENT_CLAIMED;
return toret;
}
int run_text_script(ccInstance*sci,char*tsname) {
if (strcmp(tsname, REP_EXEC_NAME) == 0) {
// run module rep_execs
int room_changes_was = play.room_changes;
int restore_game_count_was = gameHasBeenRestored;
for (int kk = 0; kk < numScriptModules; kk++) {
if (moduleRepExecAddr[kk] != NULL)
run_script_function_if_exist(moduleInst[kk], tsname, 0, 0, 0);
if ((room_changes_was != play.room_changes) ||
(restore_game_count_was != gameHasBeenRestored))
return 0;
}
}
int toret = run_script_function_if_exist(sci, tsname, 0, 0, 0);
if ((toret == -18) && (sci == roominst)) {
// functions in room script must exist
quitprintf("prepare_script: error %d (%s) trying to run '%s' (Room %d)",toret,ccErrorString,tsname, displayed_room);
}
return toret;
}
int run_claimable_event(char *tsname, bool includeRoom, int numParams, long param1, long param2, bool *eventWasClaimed) {
*eventWasClaimed = true;
// Run the room script function, and if it is not claimed,
// then run the main one
// We need to remember the eventClaimed variable's state, in case
// this is a nested event
int eventClaimedOldValue = eventClaimed;
eventClaimed = EVENT_INPROGRESS;
int toret;
if (includeRoom) {
toret = run_script_function_if_exist(roominst, tsname, numParams, param1, param2);
if (eventClaimed == EVENT_CLAIMED) {
eventClaimed = eventClaimedOldValue;
return toret;
}
}
// run script modules
for (int kk = 0; kk < numScriptModules; kk++) {
toret = run_script_function_if_exist(moduleInst[kk], tsname, numParams, param1, param2);
if (eventClaimed == EVENT_CLAIMED) {
eventClaimed = eventClaimedOldValue;
return toret;
}
}
eventClaimed = eventClaimedOldValue;
*eventWasClaimed = false;
return 0;
}
int run_text_script_iparam(ccInstance*sci,char*tsname,long iparam) {
if ((strcmp(tsname, "on_key_press") == 0) || (strcmp(tsname, "on_mouse_click") == 0)) {
bool eventWasClaimed;
int toret = run_claimable_event(tsname, true, 1, iparam, 0, &eventWasClaimed);
if (eventWasClaimed)
return toret;
}
return run_script_function_if_exist(sci, tsname, 1, iparam, 0);
}
int run_text_script_2iparam(ccInstance*sci,char*tsname,long iparam,long param2) {
if (strcmp(tsname, "on_event") == 0) {
bool eventWasClaimed;
int toret = run_claimable_event(tsname, true, 2, iparam, param2, &eventWasClaimed);
if (eventWasClaimed)
return toret;
}
// response to a button click, better update guis
if (strnicmp(tsname, "interface_click", 15) == 0)
guis_need_update = 1;
int toret = run_script_function_if_exist(sci, tsname, 2, iparam, param2);
// tsname is no longer valid, because run_script_function_if_exist might
// have restored a save game and freed the memory. Therefore don't
// attempt any strcmp's here
tsname = NULL;
return toret;
}
void remove_screen_overlay_index(int cc) {
int dd;
if (screenover[cc].pic!=NULL)
wfreeblock(screenover[cc].pic);
screenover[cc].pic=NULL;
if (screenover[cc].bmp != NULL)
gfxDriver->DestroyDDB(screenover[cc].bmp);
screenover[cc].bmp = NULL;
if (screenover[cc].type==OVER_COMPLETE) is_complete_overlay--;
if (screenover[cc].type==OVER_TEXTMSG) is_text_overlay--;
// if the script didn't actually use the Overlay* return
// value, dispose of the pointer
if (screenover[cc].associatedOverlayHandle)
ccAttemptDisposeObject(screenover[cc].associatedOverlayHandle);
numscreenover--;
for (dd = cc; dd < numscreenover; dd++)
screenover[dd] = screenover[dd+1];
// if an overlay before the sierra-style speech one is removed,
// update the index
if (face_talking > cc)
face_talking--;
}
void remove_screen_overlay(int type) {
int cc;
for (cc=0;cc<numscreenover;cc++) {
if (screenover[cc].type==type) ;
else if (type==-1) ;
else continue;
remove_screen_overlay_index(cc);
cc--;
}
}
int find_overlay_of_type(int typ) {
int ww;
for (ww=0;ww<numscreenover;ww++) {
if (screenover[ww].type == typ) return ww;
}
return -1;
}
int add_screen_overlay(int x,int y,int type,block piccy, bool alphaChannel = false) {
if (numscreenover>=MAX_SCREEN_OVERLAYS)
quit("too many screen overlays created");
if (type==OVER_COMPLETE) is_complete_overlay++;
if (type==OVER_TEXTMSG) is_text_overlay++;
if (type==OVER_CUSTOM) {
int uu; // find an unused custom ID
for (uu=OVER_CUSTOM+1;uu<OVER_CUSTOM+100;uu++) {
if (find_overlay_of_type(uu) == -1) { type=uu; break; }
}
}
screenover[numscreenover].pic=piccy;
screenover[numscreenover].bmp = gfxDriver->CreateDDBFromBitmap(piccy, alphaChannel);
screenover[numscreenover].x=x;
screenover[numscreenover].y=y;
screenover[numscreenover].type=type;
screenover[numscreenover].timeout=0;
screenover[numscreenover].bgSpeechForChar = -1;
screenover[numscreenover].associatedOverlayHandle = 0;
screenover[numscreenover].hasAlphaChannel = alphaChannel;
screenover[numscreenover].positionRelativeToScreen = true;
numscreenover++;
return numscreenover-1;
}
void my_fade_out(int spdd) {
EndSkippingUntilCharStops();
if (play.fast_forward)
return;
if (play.screen_is_faded_out == 0)
gfxDriver->FadeOut(spdd, play.fade_to_red, play.fade_to_green, play.fade_to_blue);
if (game.color_depth > 1)
play.screen_is_faded_out = 1;
}
void my_fade_in(PALLETE p, int speed) {
if (game.color_depth > 1) {
set_palette (p);
play.screen_is_faded_out = 0;
if (play.no_hicolor_fadein) {
return;
}
}
gfxDriver->FadeIn(speed, p, play.fade_to_red, play.fade_to_green, play.fade_to_blue);
}
int GetMaxScreenHeight () {
int maxhit = BASEHEIGHT;
if ((maxhit == 200) || (maxhit == 400))
{
// uh ... BASEHEIGHT depends on Native Coordinates setting so be careful
if ((usetup.want_letterbox) && (thisroom.height > maxhit))
maxhit = divide_down_coordinate(multiply_up_coordinate(maxhit) + get_fixed_pixel_size(40));
}
return maxhit;
}
block fix_bitmap_size(block todubl) {
int oldw=todubl->w, oldh=todubl->h;
int newWidth = multiply_up_coordinate(thisroom.width);
int newHeight = multiply_up_coordinate(thisroom.height);
if ((oldw == newWidth) && (oldh == newHeight))
return todubl;
// block tempb=create_bitmap(scrnwid,scrnhit);
block tempb=create_bitmap_ex(bitmap_color_depth(todubl), newWidth, newHeight);
set_clip(tempb,0,0,tempb->w-1,tempb->h-1);
set_clip(todubl,0,0,oldw-1,oldh-1);
clear(tempb);
stretch_blit(todubl,tempb,0,0,oldw,oldh,0,0,tempb->w,tempb->h);
destroy_bitmap(todubl); todubl=tempb;
return todubl;
}
//#define _get_script_data_stack_size() (256*sizeof(int)+((int*)&scrpt[10*4])[0]+((int*)&scrpt[12*4])[0])
//#define _get_script_data_stack_size(instac) ((int*)instac->code)[10]
block temp_virtual = NULL;
color old_palette[256];
void current_fade_out_effect () {
if (platform->RunPluginHooks(AGSE_TRANSITIONOUT, 0))
return;
// get the screen transition type
int theTransition = play.fade_effect;
// was a temporary transition selected? if so, use it
if (play.next_screen_transition >= 0)
theTransition = play.next_screen_transition;
if ((theTransition == FADE_INSTANT) || (play.screen_tint >= 0)) {
if (!play.keep_screen_during_instant_transition)
wsetpalette(0,255,black_palette);
}
else if (theTransition == FADE_NORMAL)
{
my_fade_out(5);
}
else if (theTransition == FADE_BOXOUT)
{
gfxDriver->BoxOutEffect(true, get_fixed_pixel_size(16), 1000 / GetGameSpeed());
play.screen_is_faded_out = 1;
}
else
{
get_palette(old_palette);
temp_virtual = create_bitmap_ex(bitmap_color_depth(abuf),virtual_screen->w,virtual_screen->h);
//blit(abuf,temp_virtual,0,0,0,0,abuf->w,abuf->h);
gfxDriver->GetCopyOfScreenIntoBitmap(temp_virtual);
}
}
void save_room_data_segment () {
if (croom->tsdatasize > 0)
free(croom->tsdata);
croom->tsdata = NULL;
croom->tsdatasize = roominst->globaldatasize;
if (croom->tsdatasize > 0) {
croom->tsdata=(char*)malloc(croom->tsdatasize+10);
ccFlattenGlobalData (roominst);
memcpy(croom->tsdata,&roominst->globaldata[0],croom->tsdatasize);
ccUnFlattenGlobalData (roominst);
}
}
void unload_old_room() {
int ff;
// if switching games on restore, don't do this
if (displayed_room < 0)
return;
platform->WriteDebugString("Unloading room %d", displayed_room);
current_fade_out_effect();
clear(abuf);
for (ff=0;ff<croom->numobj;ff++)
objs[ff].moving = 0;
if (!play.ambient_sounds_persist) {
for (ff = 1; ff < MAX_SOUND_CHANNELS; ff++)
StopAmbientSound(ff);
}
cancel_all_scripts();
numevents = 0; // cancel any pending room events
if (roomBackgroundBmp != NULL)
{
gfxDriver->DestroyDDB(roomBackgroundBmp);
roomBackgroundBmp = NULL;
}
if (croom==NULL) ;
else if (roominst!=NULL) {
save_room_data_segment();
ccFreeInstance(roominstFork);
ccFreeInstance(roominst);
roominstFork = NULL;
roominst=NULL;
}
else croom->tsdatasize=0;
memset(&play.walkable_areas_on[0],1,MAX_WALK_AREAS+1);
play.bg_frame=0;
play.bg_frame_locked=0;
play.offsets_locked=0;
remove_screen_overlay(-1);
if (raw_saved_screen != NULL) {
wfreeblock(raw_saved_screen);
raw_saved_screen = NULL;
}
for (ff = 0; ff < MAX_BSCENE; ff++)
play.raw_modified[ff] = 0;
for (ff = 0; ff < thisroom.numLocalVars; ff++)
croom->interactionVariableValues[ff] = thisroom.localvars[ff].value;
// wipe the character cache when we change rooms
for (ff = 0; ff < game.numcharacters; ff++) {
if (charcache[ff].inUse) {
destroy_bitmap (charcache[ff].image);
charcache[ff].image = NULL;
charcache[ff].inUse = 0;
}
// ensure that any half-moves (eg. with scaled movement) are stopped
charextra[ff].xwas = INVALID_X;
}
play.swap_portrait_lastchar = -1;
for (ff = 0; ff < croom->numobj; ff++) {
// un-export the object's script object
if (objectScriptObjNames[ff][0] == 0)
continue;
ccRemoveExternalSymbol(objectScriptObjNames[ff]);
}
for (ff = 0; ff < MAX_HOTSPOTS; ff++) {
if (thisroom.hotspotScriptNames[ff][0] == 0)
continue;
ccRemoveExternalSymbol(thisroom.hotspotScriptNames[ff]);
}
// clear the object cache
for (ff = 0; ff < MAX_INIT_SPR; ff++) {
if (objcache[ff].image != NULL) {
destroy_bitmap (objcache[ff].image);
objcache[ff].image = NULL;
}
}
// clear the actsps buffers to save memory, since the
// objects/characters involved probably aren't on the
// new screen. this also ensures all cached data is flushed
for (ff = 0; ff < MAX_INIT_SPR + game.numcharacters; ff++) {
if (actsps[ff] != NULL)
destroy_bitmap(actsps[ff]);
actsps[ff] = NULL;
if (actspsbmp[ff] != NULL)
gfxDriver->DestroyDDB(actspsbmp[ff]);
actspsbmp[ff] = NULL;
if (actspswb[ff] != NULL)
destroy_bitmap(actspswb[ff]);
actspswb[ff] = NULL;
if (actspswbbmp[ff] != NULL)
gfxDriver->DestroyDDB(actspswbbmp[ff]);
actspswbbmp[ff] = NULL;
actspswbcache[ff].valid = 0;
}
// if Hide Player Character was ticked, restore it to visible
if (play.temporarily_turned_off_character >= 0) {
game.chars[play.temporarily_turned_off_character].on = 1;
play.temporarily_turned_off_character = -1;
}
}
void redo_walkable_areas() {
// since this is an 8-bit memory bitmap, we can just use direct
// memory access
if ((!is_linear_bitmap(thisroom.walls)) || (bitmap_color_depth(thisroom.walls) != 8))
quit("Walkable areas bitmap not linear");
blit(walkareabackup, thisroom.walls, 0, 0, 0, 0, thisroom.walls->w, thisroom.walls->h);
int hh,ww;
for (hh=0;hh<walkareabackup->h;hh++) {
for (ww=0;ww<walkareabackup->w;ww++) {
// if (play.walkable_areas_on[_getpixel(thisroom.walls,ww,hh)]==0)
if (play.walkable_areas_on[thisroom.walls->line[hh][ww]]==0)
_putpixel(thisroom.walls,ww,hh,0);
}
}
}
void generate_light_table() {
int cc;
if ((game.color_depth == 1) && (color_map == NULL)) {
// in 256-col mode, check if we need the light table this room
for (cc=0;cc < MAX_REGIONS;cc++) {
if (thisroom.regionLightLevel[cc] < 0) {
create_light_table(&maincoltable,palette,0,0,0,NULL);
color_map=&maincoltable;
break;
}
}
}
}
void SetAreaLightLevel(int area, int brightness) {
if ((area < 0) || (area > MAX_REGIONS))
quit("!SetAreaLightLevel: invalid region");
if (brightness < -100) brightness = -100;
if (brightness > 100) brightness = 100;
thisroom.regionLightLevel[area] = brightness;
// disable RGB tint for this area
thisroom.regionTintLevel[area] &= ~TINT_IS_ENABLED;
generate_light_table();
DEBUG_CONSOLE("Region %d light level set to %d", area, brightness);
}
void Region_SetLightLevel(ScriptRegion *ssr, int brightness) {
SetAreaLightLevel(ssr->id, brightness);
}
int Region_GetLightLevel(ScriptRegion *ssr) {
return thisroom.regionLightLevel[ssr->id];
}
void SetRegionTint (int area, int red, int green, int blue, int amount) {
if ((area < 0) || (area > MAX_REGIONS))
quit("!SetRegionTint: invalid region");
if ((red < 0) || (red > 255) || (green < 0) || (green > 255) ||
(blue < 0) || (blue > 255)) {
quit("!SetRegionTint: RGB values must be 0-255");
}
// originally the value was passed as 0
if (amount == 0)
amount = 100;
if ((amount < 1) || (amount > 100))
quit("!SetRegionTint: amount must be 1-100");
DEBUG_CONSOLE("Region %d tint set to %d,%d,%d", area, red, green, blue);
/*red -= 100;
green -= 100;
blue -= 100;*/
unsigned char rred = red;
unsigned char rgreen = green;
unsigned char rblue = blue;
thisroom.regionTintLevel[area] = TINT_IS_ENABLED;
thisroom.regionTintLevel[area] |= rred & 0x000000ff;
thisroom.regionTintLevel[area] |= (int(rgreen) << 8) & 0x0000ff00;
thisroom.regionTintLevel[area] |= (int(rblue) << 16) & 0x00ff0000;
thisroom.regionLightLevel[area] = amount;