diff options
60 files changed, 25143 insertions, 0 deletions
diff --git a/sword2/.cvsignore b/sword2/.cvsignore new file mode 100644 index 0000000000..39a06683b7 --- /dev/null +++ b/sword2/.cvsignore @@ -0,0 +1 @@ +.deps diff --git a/sword2/anims.cpp b/sword2/anims.cpp new file mode 100644 index 0000000000..ad3f5f80ae --- /dev/null +++ b/sword2/anims.cpp @@ -0,0 +1,911 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------------------------------- +// A more intelligent version of the old ANIMS.C +// All this stuff by James +// DON'T TOUCH! +//------------------------------------------------------------------------------------------------------------- + +//#include <io.h> // for access() +#include <stdio.h> // for sprintf +#include <string.h> + +#include "common/scummsys.h" +#include "driver/driver96.h" +#include "anims.h" +#include "build_display.h" // for DisplayMsg() +#include "console.h" +#include "controls.h" // for 'speechSelected' & 'subtitles' +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "interpreter.h" +#include "layers.h" // for 'this_screen' structure - for restoring palette in FN_play_sequence +#include "maketext.h" // for MakeTextSprite used by FN_play_sequence ultimately +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "sword2.h" // for Close_game() +#include "sync.h" +#include "sound.h" // for Speech stuff. + +//------------------------------------------------------------------------------------------------------------- +uint32 smackerLeadOut=0; // stores resource id of wav to use as lead-out from smacker + +//------------------------------------------------------------------------------------------------------------- +// local function prototypes + +int32 Animate(int32 *params, uint8 reverse_flag); +int32 Mega_table_animate(int32 *params, uint8 reverse_flag); + +//------------------------------------------------------------------------------------------------------------- +int32 FN_anim(int32 *params) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 resource id of animation file + + return Animate(params,0); // 0 means normal forward anim +} +//------------------------------------------------------------------------------------------------------------- +int32 FN_reverse_anim(int32 *params) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 resource id of animation file + + return Animate(params,1); // 1 means reverse anim +} +//------------------------------------------------------------------------------------------------------------- +int32 FN_mega_table_anim(int32 *params) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to animation table + + return Mega_table_animate(params,0); // 0 means normal forward anim +} +//------------------------------------------------------------------------------------------------------------- +int32 FN_reverse_mega_table_anim(int32 *params) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to animation table + + return Mega_table_animate(params,1); // 1 means reverse anim +} +//------------------------------------------------------------------------------------------------------------- +int32 Animate(int32 *params, uint8 reverse_flag) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 resource id of animation file + + Object_logic *ob_logic; + Object_graphic *ob_graphic; + uint8 *anim_file; + _animHeader *anim_head; + int32 res = params[2]; + + #ifdef _DEBUG + _standardHeader *head; // for animation testing & checking for correct file type + #endif + + //---------------------------------------------------------------------------------------- + // read the main parameters + + ob_logic = (Object_logic *) params[0]; // param 0 is pointer to normal structure + ob_graphic = (Object_graphic *) params[1]; // param 1 is pointer to graphic structure + + //---------------------------------------------------------------------------------------- + // if this is the start of the anim, set up the first frame + + if (ob_logic->looping==0) + { + //----------------------------------------------------------- + #ifdef _DEBUG + // For testing all anims! (James18apr97) + // A script loop can send every resource number to the anim function + // & it will only run the valid ones + // See 'testing_routines' object in George's Player Character section of linc + if (SYSTEM_TESTING_ANIMS) + { + if (res_man.Res_check_valid(res)) // if the resource number is within range & it's not a null resource + { + head = (_standardHeader*) res_man.Res_open(res); // open the resource + res_man.Res_close(res); // can close immediately - we've got a pointer to the header + + if (head->fileType!=ANIMATION_FILE) // if it's not an animation file + { + FN_no_sprite(params+1); // switch off the sprite + return(IR_STOP); // don't animate - just continue script next cycle + } + } + else + { // not a valid resource number + FN_no_sprite(params+1); // switch off the sprite + return(IR_STOP); // don't animate - just continue script next cycle + } + + FN_sort_sprite(params+1); // switch on the sprite + } + #endif + //----------------------------------------------------------- + + //--------------------- + #ifdef _DEBUG + // check that we haven't been passed a zero resource number + if (res==0) + Con_fatal_error("Animate: %s (id %d) passed zero anim resource (%s line %u)", FetchObjectName(ID), ID, __FILE__, __LINE__); + #endif + //--------------------- + + anim_file = res_man.Res_open(res); // open anim file + + //--------------------- + #ifdef _DEBUG + // check this this resource is actually an animation file! + head = (_standardHeader*) anim_file; + if (head->fileType!=ANIMATION_FILE) // if it's not an animation file + Con_fatal_error("Animate: %s (%d) is not an anim! (%s line %u)", FetchObjectName(res), res, __FILE__, __LINE__); + #endif + //--------------------- + + anim_head = FetchAnimHeader( anim_file ); // point to anim header + + //--------------------- + /* + #ifdef _DEBUG + // check there's at least one frame + if (anim_head->noAnimFrames==0) + Con_fatal_error("Animate: %s (%d) has zero frame count! (%s line %u)", FetchObjectName(res), res, __FILE__, __LINE__); + #endif + */ + //--------------------- + + ob_logic->looping = 1; // now running an anim, looping back to this 'FN' call again + ob_graphic->anim_resource = res; // param 2 is id of animation resource + + if (reverse_flag) // if a reverse anim + ob_graphic->anim_pc = anim_head->noAnimFrames-1; // start on last frame + else // forward anim + ob_graphic->anim_pc = 0; // start on first frame + } + //------------------------------------------------------------------------------------------------------- + // otherwise, if we've received a sync, return to script immediately + + else if (Get_sync()) // returns sync value if one has been sent to current 'id', otherwise 0 + { +// Zdebug("**sync stopped %d**", ID); + ob_logic->looping = 0; // if sync received, anim finishes right now (remaining on last frame) + return(IR_CONT); // quit anim but continue script + } + //------------------------------------------------------------------------------------------------------- + // otherwise (not first frame & not received a sync), set up the next frame of the anim + else + { + anim_file = res_man.Res_open(ob_graphic->anim_resource); // open anim file + anim_head = FetchAnimHeader( anim_file ); // point to anim header + + if (reverse_flag) // if a reverse anim + ob_graphic->anim_pc--; // decrement the anim frame number + else // normal forward anim + ob_graphic->anim_pc++; // increment the anim frame number + } + //------------------------------------------------------------------------------------------------------- + // check for end of anim + + if (reverse_flag) // if a reverse anim + { + if (ob_graphic->anim_pc == 0) // reached the first frame of the anim + ob_logic->looping = 0; // anim finishes on this frame + } + else // normal forward anim + { + if (ob_graphic->anim_pc == (int32)(anim_head->noAnimFrames-1)) // reached the last frame of the anim + ob_logic->looping = 0; // anim finishes on this frame + } + //------------------------------------------------------------------------------------------------------- + // close the anim file + + res_man.Res_close(ob_graphic->anim_resource); // close anim file + + //------------------------------------------------------------------------------------------------------- + // check if we want the script to loop back & call this function again + + if (ob_logic->looping) + return(IR_REPEAT); // drop out of script, but call this function again next cycle + else + return(IR_STOP); // drop out of script + + //------------------------------------------------------------------------------------------------------- +} +//------------------------------------------------------------------------------------------------------------- +int32 Mega_table_animate(int32 *params, uint8 reverse_flag) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to animation table + + Object_logic *ob_logic; + Object_mega *ob_mega; + uint32 *anim_table; + int32 pars[5]; + + //---------------------------------------------------------------------------------------- + // if this is the start of the anim, read the anim table to get the appropriate anim resource + + ob_logic = (Object_logic *) params[0]; // param 0 is pointer to logic structure + + if (ob_logic->looping==0) + { + ob_mega = (Object_mega *) params[2]; // param 2 is pointer to mega structure + + anim_table = (uint32*)params[3]; + pars[2] = anim_table[ob_mega->current_dir]; // appropriate anim resource is in 'table[direction]' + } + + //------------------------------------------------------------------------------------------------------- + // set up the rest of the parameters for FN_anim() + + pars[0] = params[0]; + pars[1] = params[1]; + // pars[2] only needed setting at the start of the anim + + //------------------------------------------------------------------------------------------------------- + + return Animate(pars, reverse_flag); // call Animate() with these params +} +//------------------------------------------------------------------------------------------------------------- +int32 FN_set_frame(int32 *params) +{ + // params: 0 pointer to object's graphic structure + // 1 resource id of animation file + // 2 frame flag (0=first 1=last) + + Object_graphic *ob_graphic; + uint8 *anim_file; + _animHeader *anim_head; + int32 res = params[1]; + + #ifdef _DEBUG + _standardHeader *head; // for checking for correct file type + #endif + + //--------------------- + #ifdef _DEBUG + // check that we haven't been passed a zero resource number + if (res==0) + Con_fatal_error("FN_set_frame: %s (id %d) passed zero anim resource (%s line %u)", FetchObjectName(ID), ID, __FILE__, __LINE__); + #endif + //--------------------- + + //---------------------------------------------------------------------------------------- + // open the resource (& check it's valid) + + anim_file = res_man.Res_open(res); // open anim file + + //--------------------- + #ifdef _DEBUG + // check this this resource is actually an animation file! + head = (_standardHeader*) anim_file; + if (head->fileType!=ANIMATION_FILE) // if it's not an animation file + Con_fatal_error("FN_set_frame: %s (%d) is not an anim! (%s line %u)", FetchObjectName(res), res, __FILE__, __LINE__); + #endif + //--------------------- + + anim_head = FetchAnimHeader( anim_file ); // set up pointer to the animation header + + //--------------------- + /* + #ifdef _DEBUG + // check there's at least one frame + if (anim_head->noAnimFrames==0) + Con_fatal_error("FN_set_frame: %s (%d) has zero frame count! (%s line %u)", FetchObjectName(res), res, __FILE__, __LINE__); + #endif + */ + //--------------------- + + //---------------------------------------------------------------------------------------- + // set up anim resource in graphic object + + ob_graphic = (Object_graphic *) params[0]; // param 0 is pointer to the object's graphic structure + + ob_graphic->anim_resource = res; // param 2 is id of animation resource + + //---------------------------------------------------------------------------------------- + + if (params[2]) // frame flag is non-zero + ob_graphic->anim_pc = anim_head->noAnimFrames-1; // last frame + else // frame flag is 0 + ob_graphic->anim_pc = 0; // first frame + + //------------------------------------------------------------------------------------------------------- + // close the anim file + + res_man.Res_close(ob_graphic->anim_resource); // close anim file + + //------------------------------------------------------------------------------------------------------- + + return(IR_CONT); // drop out of script +} + +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_no_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= NO_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_back_par0_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= BGP0_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_back_par1_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= BGP1_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_back_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= BACK_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_sort_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= SORT_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_fore_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= FORE_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_fore_par0_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= FGP0_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_fore_par1_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0xffff0000; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= FGP1_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_shaded_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0x0000ffff; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= SHADED_SPRITE; + + // note that drivers may still shade mega frames automatically, even when not sent 'RDSPR_SHADOW' + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_unshaded_sprite(int32 *params) +{ + // params 0 pointer to object's graphic structure + + Object_graphic *ob_graphic = (Object_graphic *) params[0]; + + ob_graphic->type &= 0x0000ffff; // remove previous status (but don't affect the shading upper-word) + ob_graphic->type |= UNSHADED_SPRITE; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +// Notes on PlaySmacker() + +// 1st param is filename of sequence file +// 2nd param is a pointer to a null-terminated array of pointers to _movieTextObject structures + + +//int32 PlaySmacker(char *filename, _movieTextObject *textObjects[]); + +// typedef struct +// { +// uint16 startFrame; +// uint16 endFrame; +// _spriteInfo *textSprite; +// _wavHeader *speech; +// } _movieTextObject; + +//--------------------------------------------------------------------------------------------------------------------- +// FOR TEXT LINES IN SEQUENCE PLAYER (James22may97) + +#define MAX_SEQUENCE_TEXT_LINES 15 + +typedef struct +{ + uint32 textNumber; + uint16 startFrame; + uint16 endFrame; + mem *text_mem; + mem *speech_mem; +} _sequenceTextInfo; + +static _sequenceTextInfo sequence_text_list[MAX_SEQUENCE_TEXT_LINES]; +uint32 sequenceTextLines=0; // keeps count of number of text lines to disaply during the sequence + +//------------------------------------------------------------------------------------------------------------- + +int32 FN_add_sequence_text(int32 *params) // (James22may97) +{ +// params 0 text number +// 1 frame number to start the text displaying +// 2 frame number to stop the text dispalying + + #ifdef _DEBUG + if (sequenceTextLines == MAX_SEQUENCE_TEXT_LINES) + Con_fatal_error("FN_add_sequence_text ran out of lines (%s line %u)",__FILE__,__LINE__); + #endif + + sequence_text_list[sequenceTextLines].textNumber = params[0]; + sequence_text_list[sequenceTextLines].startFrame = params[1]; + sequence_text_list[sequenceTextLines].endFrame = params[2]; + sequenceTextLines++; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- + +// speech sample code added by James on 16july97 +void CreateSequenceSpeech(_movieTextObject *sequenceText[]) // (James23may97) +{ + uint32 line; + _frameHeader *frame; + uint32 local_text; + uint32 text_res; + uint8 *text; + int16 wavId; // ie. offical text number (actor text number) + uint8 speechRunning; + char speechFile[256]; + int32 wavSize; + + + for (line=0; line < sequenceTextLines; line++) // for each sequence text line that's been logged + { + sequenceText[line] = new _movieTextObject; // allocate this structure + + sequenceText[line]->startFrame = sequence_text_list[line].startFrame; + sequenceText[line]->endFrame = sequence_text_list[line].endFrame; + + //----------------------------------------------------------- + // pull out the text line to get the official text number (for wav id) + + text_res = sequence_text_list[line].textNumber/SIZE; + local_text = sequence_text_list[line].textNumber&0xffff; + + text = FetchTextLine( res_man.Res_open(text_res), local_text ); // open text resource & get the line + memcpy(&wavId, text, 2); // this works on PSX & PC + + res_man.Res_close(text_res); // now ok to close the text file + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"(%d) SEQUENCE TEXT: %s", *(uint16*)text, text+2); // 1st word of text line is the official line number + #endif + //-------------------------------------- + // is it to be speech or subtitles or both? + + speechRunning=0; // assume not running until know otherwise + sequence_text_list[line].speech_mem = NULL; + sequenceText[line]->speech = NULL; + + if (speechSelected) // speech is selected, so try that first + { + //------------------------------ + // set up path to speech cluster + // first checking if we have speech1.clu or speech2.clu in current directory (for translators to test) + + #ifdef _WEBDEMO // (James 03oct97) + strcpy(speechFile,"SPEECH.CLU"); + #else + + #ifdef _DEBUG + if ((res_man.WhichCd()==1) && (!access("speech1.clu",0))) // if 0 ie. if it's there + { + strcpy(speechFile,"speech1.clu"); + } + else if ((res_man.WhichCd()==2) && (!access("speech2.clu",0))) // if 0 ie. if it's there + { + strcpy(speechFile,"speech2.clu"); + } + else + #endif // _DEBUG + { + strcpy(speechFile,res_man.GetCdPath()); + strcat(speechFile,"CLUSTERS\\SPEECH.CLU"); + } + + #endif // _WEBDEMO + //------------------------------ + + wavSize = GetCompSpeechSize(speechFile, wavId); // returns size of decompressed wav, or 0 if wav not found + if (wavSize) // if we've got the wav + { + // allocate memory for speech buffer + sequence_text_list[line].speech_mem = Twalloc( wavSize, MEM_locked, UID_temp ); // last param is an optional id for type of mem block + + if (sequence_text_list[line].speech_mem) // if mem allocated ok (should be fine, but worth checking) + { + if (PreFetchCompSpeech(speechFile, wavId, sequence_text_list[line].speech_mem->ad) == RD_OK) // Load speech & decompress to our buffer + { + Float_mem (sequence_text_list[line].speech_mem); // now float this buffer so we can make space for the next text sprites and/or speech samples + speechRunning=1; // ok, we've got speech! + } + else // whoops, sample didn't load & decompress for some reason... + { + Free_mem (sequence_text_list[line].speech_mem); // may as well free up this speech buffer now, rather than in ClearSequenceSpeech(); + sequence_text_list[line].speech_mem = NULL; // so we know that it's free'd + } + } + } + } + + if (subtitles || (speechRunning==0)) // if we want subtitles, or speech failed to load + { + text = FetchTextLine( res_man.Res_open(text_res), local_text ); // open text resource & get the line + // mem* MakeTextSprite( uint8 *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes ); + sequence_text_list[line].text_mem = MakeTextSprite( text+2, 600, 255, speech_font_id ); // make the sprite + // 'text+2' to skip the first 2 bytes which form the line reference number + // NB. The mem block containing the text sprite is currently FLOATING! + + res_man.Res_close(text_res); // ok to close the text resource now + } + else + { + sequence_text_list[line].text_mem = NULL; + sequenceText[line]->textSprite = NULL; + } + //-------------------------------------- + } + + sequenceText[sequenceTextLines] = NULL; // for drivers: NULL-terminate the array of pointers to _movieTextObject's + + //--------------------------------------- + // now lock all the memory blocks containing text sprites & speech samples + // and set up the pointers to them, etc, for the drivers + + for (line=0; line < sequenceTextLines; line++) + { + // text sprites: + if (sequence_text_list[line].text_mem) // if we've made a text sprite for this line... + { + Lock_mem (sequence_text_list[line].text_mem); + // now fill out the _spriteInfo structure in the _movieTextObjectStructure + + frame = (_frameHeader*) sequence_text_list[line].text_mem->ad; + + sequenceText[line]->textSprite = new _spriteInfo; + + sequenceText[line]->textSprite->x = 320 - frame->width/2; // centred + sequenceText[line]->textSprite->y = 440 - frame->height; // at bottom of screen + sequenceText[line]->textSprite->w = frame->width; + sequenceText[line]->textSprite->h = frame->height; + sequenceText[line]->textSprite->scale = 0; + sequenceText[line]->textSprite->scaledWidth = 0; + sequenceText[line]->textSprite->scaledHeight= 0; + sequenceText[line]->textSprite->type = RDSPR_DISPLAYALIGN+RDSPR_TRANS+RDSPR_NOCOMPRESSION; + sequenceText[line]->textSprite->blend = 0; + sequenceText[line]->textSprite->data = sequence_text_list[line].text_mem->ad+sizeof(_frameHeader); + sequenceText[line]->textSprite->colourTable = 0; + } + + // speech samples: + if (sequence_text_list[line].speech_mem) // if we've loaded a speech sample for this line... + { + Lock_mem (sequence_text_list[line].speech_mem); + sequenceText[line]->speech = (_wavHeader *)sequence_text_list[line].speech_mem->ad; // for drivers: set up pointer to decompressed wav in memory + } + } + //--------------------------------------- +} +//--------------------------------------------------------------------------------------------------------------------- + +// speech sample code added by James on 16july97 +void ClearSequenceSpeech(_movieTextObject *textSprites[]) // (James27may97) +{ + uint32 line; + + for (line=0; line < sequenceTextLines; line++) + { + delete (textSprites[line]); // free up the memory used by this _movieTextObject + + if (sequence_text_list[line].text_mem) + Free_mem (sequence_text_list[line].text_mem); // free up the mem block containing this text sprite + + if (sequence_text_list[line].speech_mem) + Free_mem (sequence_text_list[line].speech_mem); // free up the mem block containing this speech sample + } + + sequenceTextLines=0; // IMPORTANT! Reset the line count ready for the next sequence! +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_smacker_lead_in(int32 *params) // James(21july97) +{ + uint8 *leadIn; + uint32 rv; +#ifdef _DEBUG + _standardHeader *header; +#endif + + + leadIn = res_man.Res_open(params[0]); + + //----------------------------------------- + #ifdef _DEBUG + header = (_standardHeader*)leadIn; + if (header->fileType != WAV_FILE) + Con_fatal_error("FN_smacker_lead_in() given invalid resource (%s line %u)",__FILE__,__LINE__); + #endif + //----------------------------------------- + + leadIn += sizeof(_standardHeader); + rv = PlayFx( 0, leadIn, 0, 0, RDSE_FXLEADIN ); // wav data gets copied to sound memory + + //----------------------------------------- + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: PlayFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif + //----------------------------------------- + + res_man.Res_close(params[0]); + + FN_stop_music(NULL); // fade out any music that is currently playing (James22july97) + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_smacker_lead_out(int32 *params) // James(21july97) +{ + smackerLeadOut = params[0]; // ready for use in FN_play_sequence + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- + +int32 FN_play_sequence(int32 *params) // James(09apr97) +{ + // params 0 pointer to null-terminated ascii filename + // params 1 number of frames in the sequence, used for PSX. + + char filename[30]; + uint32 rv; // drivers return value + _movieTextObject *sequenceSpeechArray[MAX_SEQUENCE_TEXT_LINES+1]; + uint8 *leadOut = NULL; +#ifdef _DEBUG + _standardHeader *header; +#endif + +#ifdef _MUTE_SMACKERS + uint32 musicMuteStatus; +#endif + + //---------------------------------- + // In the case where smackers are crashing but the rest of the game is fine, + // the "Skip Smackers" executable will display a message giving the smacker + // file name rather than actually playing it. + // Then the user can switch tasks & view the smacker using the stand-alone player! + // This has got to be the biggest fudge in the history of computer games. + + #ifdef _SKIP_SMACKERS + uint8 message[30]; + + sprintf((char*)message,"SKIPPING SMACKER: \"%s.smk\"", (char *)params[0]); + DisplayMsg(message, 3); // 3 is duration in seconds + RemoveMsg(); + sequenceTextLines=0; // IMPORTANT - clear this so it doesn't overflow! + return(IR_CONT); // continue script now; don't play smacker! + #endif + //---------------------------------- + // Another attempt to prevent the smacker crash + // This time muting the music during the smacker + // - in case that's what's causing the crash + + #ifdef _MUTE_SMACKERS + musicMuteStatus = IsMusicMute(); // keep note of what mute status was to start with + MuteMusic(1); // mute the music - we'll set it back to 'musicMuteStatus' later + #endif + //---------------------------------- + + Zdebug("FN_play_sequence(\"%s\");", params[0]); + + //-------------------------------------------------- + // check that the name paseed from script is 8 chars or less + #ifdef _DEBUG + if (strlen((char *)params[0]) > 8) + Con_fatal_error("Sequence filename too long (%s line %u)",__FILE__,__LINE__); + #endif + //-------------------------------------------------- + // add the appropriate file extension & play it + + #ifdef _WEBDEMO // (James 01oct97) + sprintf(filename,"%s.smk", (char *)params[0]); + #else + sprintf(filename,"%sSMACKS\\%s.smk", res_man.GetCdPath(), (char *)params[0]); + #endif // _WEBDEMO + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + + #ifdef _DEBUG + Zdebug(0,"PLAYING SEQUENCE \"%s\"", filename); + #endif + //-------------------------------------- + // now create the text sprites, if any (James27may97) + + if (sequenceTextLines) // if we have some text to accompany this sequence + CreateSequenceSpeech(sequenceSpeechArray); + + //-------------------------------------- + // open the lead-out music resource, if there is one + + if (smackerLeadOut) + { + leadOut = res_man.Res_open(smackerLeadOut); + + //--------------------------- + #ifdef _DEBUG + header = (_standardHeader*)leadOut; + if (header->fileType != WAV_FILE) + Con_fatal_error("FN_smacker_lead_out() given invalid resource (%s line %u)",__FILE__,__LINE__); + #endif + //--------------------------- + + leadOut += sizeof(_standardHeader); + } + + //-------------------------------------- + // play the smacker + + FN_stop_music(NULL); // don't want to carry on streaming game music when smacker starts! + PauseFxForSequence(); // pause sfx during sequence, except the one used for lead-in music + + if (sequenceTextLines) // if we have some text to accompany this sequence + rv = PlaySmacker(filename, sequenceSpeechArray, leadOut); + else + rv = PlaySmacker(filename, NULL, leadOut); + +/* we don't have this call - khalek + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q during the smacker + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } +*/ + + UnpauseFx(); // unpause sound fx again, in case we're staying in same location + + //-------------------------------------- + // close the lead-out music resource + + if (smackerLeadOut) + { + res_man.Res_close(smackerLeadOut); + smackerLeadOut=0; + } + //-------------------------- + // check the error return-value + #ifdef _DEBUG + if (rv) + Zdebug("PlaySmacker(\"%s\") returned 0x%.8x", filename, rv); + #endif + //-------------------------- + // now clear the text sprites, if any (James27may97) + + if (sequenceTextLines) // if we have some text/speech to accompany this sequence + ClearSequenceSpeech(sequenceSpeechArray); + + //-------------------------- + // now clear the screen in case the Sequence was quitted (using ESC) rather than fading down to black + + EraseBackBuffer(); // for hardware rendering + EraseSoftwareScreenBuffer(); // for software rendering + FlipScreens(); // to get the new blank screen visible + + //-------------------------------------------------- + // zero the entire palette in case we're about to fade up! + + _palEntry pal[256]; + + memset(pal, 0, 256*sizeof(_palEntry)); + SetPalette(0, 256, (uint8 *) pal, RDPAL_INSTANT); + //-------------------------------------------------- + + Zdebug("FN_play_sequence FINISHED"); + + //-------------------------------------------------- + #ifdef _MUTE_SMACKERS + MuteMusic(musicMuteStatus); // set mute status back to what it was before the sequence + #endif + //---------------------------------- + + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------------------------- diff --git a/sword2/anims.h b/sword2/anims.h new file mode 100644 index 0000000000..3b9d245a61 --- /dev/null +++ b/sword2/anims.h @@ -0,0 +1,36 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _ANIM_S +#define _ANIM_S + + +#include "driver/driver96.h" + +int32 FN_anim(int32 *params); +int32 FN_reverse_anim(int32 *params); +int32 FN_mega_table_anim(int32 *params); +int32 FN_reverse_mega_table_anim(int32 *params); +int32 FN_set_frame(int32 *params); +int32 FN_no_sprite(int32 *params); +int32 FN_back_sprite(int32 *params); +int32 FN_sort_sprite(int32 *params); +int32 FN_fore_sprite(int32 *params); + +#endif diff --git a/sword2/build_display.cpp b/sword2/build_display.cpp new file mode 100644 index 0000000000..600d0b19b7 --- /dev/null +++ b/sword2/build_display.cpp @@ -0,0 +1,1255 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +// BUILD_DISPLAY.CPP like the old spr_engi but slightly more aptly named +//------------------------------------------------------------------------------------ +//#include <mmsystem.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +//#include <windows.h> +//#include <windowsx.h> + +#include "driver/driver96.h" +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "events.h" +#include "function.h" +#include "header.h" +#include "interpreter.h" +#include "layers.h" +#include "logic.h" +#include "maketext.h" +#include "memory.h" +#include "mouse.h" +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "router.h" +#include "save_rest.h" +#include "scroll.h" +#include "sword2.h" + +//------------------------------------------------------------------------------------ + +buildit bgp0_list[MAX_bgp0_sprites]; +buildit bgp1_list[MAX_bgp1_sprites]; +buildit back_list[MAX_back_sprites]; +buildit sort_list[MAX_sort_sprites]; +buildit fore_list[MAX_fore_sprites]; +buildit fgp0_list[MAX_fgp0_sprites]; +buildit fgp1_list[MAX_fgp1_sprites]; + +uint16 sort_order[MAX_sort_sprites]; //holds the order of the sort list - i.e the list stays static and we sort this + +uint32 cur_bgp0; +uint32 cur_bgp1; +uint32 cur_back; +uint32 cur_sort; +uint32 cur_fore; +uint32 cur_fgp0; +uint32 cur_fgp1; + +#ifdef _DEBUG +uint32 largest_layer_area=0; // should be reset to zero at start of each screen change +uint32 largest_sprite_area=0; // - " - +char largest_layer_info[128] = {"largest layer: none registered"}; +char largest_sprite_info[128] = {"largest sprite: none registered"}; +#endif + +//------------------------------------------------------------------------------------ +// last palette used - so that we can restore the correct one after a pause (which dims the screen) +// - and it's not always the main screen palette that we want, eg. during the eclipse + +// This flag gets set in Start_new_palette() and SetFullPalette() + +uint32 lastPaletteRes=0; + +//------------------------------------------------------------------------------------ +// 'frames per second' counting stuff + +uint32 fps=0; +uint32 cycleTime=0; +uint32 frameCount=0; +extern uint32 mouse_status; // So I know if the control Panel can be activated - CJR 1-5-97 + +//------------------------------------------------------------------------------------ +// function prototypes not needed externally + +void Start_new_palette(void); //Tony25Sept96 + +void Register_frame(int32 *params, buildit *build_unit); // (1nov96JEL) +void Process_image(buildit *frame); +void Process_layer(uint32 layer_number); //Tony24Sept96 +void Sort_the_sort_list(void); //Tony18Sept96 + +void Send_back_par0_frames(void); //James23Jan97 +void Send_back_par1_frames(void); //James23Jan97 +void Send_back_frames(void); //Tony23Sept96 +void Send_sort_frames(void); +void Send_fore_frames(void); +void Send_fore_par0_frames(void); //James23Jan97 +void Send_fore_par1_frames(void); //James23Jan97 + + +//------------------------------------------------------------------------------------ +// +// PC Build_display +// +//------------------------------------------------------------------------------------ +void Build_display(void) //Tony21Sept96 +{ + BOOL end; + uint8 pal[12]={0,0,0,0,0,0,0,0,0,255,0,0}; + uint8 *file; + _multiScreenHeader *screenLayerTable; + +#ifdef _DEBUG // only used by console + _spriteInfo spriteInfo; + uint32 rv; // drivers error return value +#endif + + + + if ((!console_status)&&(this_screen.new_palette)) + { + Start_new_palette(); // start the layer palette fading up + + #ifdef _DEBUG // (James23jun97) + largest_layer_area=0; // should be reset to zero at start of each screen change + largest_sprite_area=0; // - " - + #endif + } + + + + if ((!console_status)&&(this_screen.background_layer_id)) // there is a valid screen to run + { + SetScrollTarget(this_screen.scroll_offset_x, this_screen.scroll_offset_y); // set the scroll position + + AnimateMouse(); // increment the mouse frame + + StartRenderCycle(); + + while (1) // START OF RENDER CYCLE + { + //---------------------------------------------------- + // clear the back buffer, before building up the new screen + // from the back forwards + + EraseBackBuffer(); + + //---------------------------------------------------- + // first background parallax + related anims + + file = res_man.Res_open(this_screen.background_layer_id); // open the screen resource + screenLayerTable = (_multiScreenHeader *) ((uint8 *) file + sizeof(_standardHeader)); + + if (screenLayerTable->bg_parallax[0]) + { + RenderParallax(FetchBackgroundParallaxLayer(file, 0), 0); + res_man.Res_close(this_screen.background_layer_id); // release the screen resource before cacheing the sprites + Send_back_par0_frames(); + } + else + res_man.Res_close(this_screen.background_layer_id); // release the screen resource + + //---------------------------------------------------- + // second background parallax + related anims + + file = res_man.Res_open(this_screen.background_layer_id); // open the screen resource + screenLayerTable = (_multiScreenHeader *) ((uint8 *) file + sizeof(_standardHeader)); + + if (screenLayerTable->bg_parallax[1]) + { + RenderParallax(FetchBackgroundParallaxLayer(file, 1), 1); + res_man.Res_close(this_screen.background_layer_id); // release the screen resource before cacheing the sprites + Send_back_par1_frames(); + } + else + res_man.Res_close(this_screen.background_layer_id); // release the screen resource + + //---------------------------------------------------- + // normal backround layer (just the one!) + + file = res_man.Res_open(this_screen.background_layer_id); // open the screen resource + RenderParallax(FetchBackgroundLayer(file), 2); + res_man.Res_close(this_screen.background_layer_id); // release the screen resource + + //---------------------------------------------------- + // sprites & layers + + Send_back_frames(); // background sprites + Sort_the_sort_list(); + Send_sort_frames(); // sorted sprites & layers + Send_fore_frames(); // foreground sprites + + //---------------------------------------------------- + // first foreground parallax + related anims + + file = res_man.Res_open(this_screen.background_layer_id); // open the screen resource + screenLayerTable = (_multiScreenHeader *) ((uint8 *) file + sizeof(_standardHeader)); + + if (screenLayerTable->fg_parallax[0]) + { + RenderParallax(FetchForegroundParallaxLayer(file, 0), 3); + res_man.Res_close(this_screen.background_layer_id); // release the screen resource before cacheing the sprites + Send_fore_par0_frames(); + } + else + res_man.Res_close(this_screen.background_layer_id); // release the screen resource + + //---------------------------------------------------- + // second foreground parallax + related anims + + file = res_man.Res_open(this_screen.background_layer_id); // open the screen resource + screenLayerTable = (_multiScreenHeader *) ((uint8 *) file + sizeof(_standardHeader)); + + if (screenLayerTable->fg_parallax[1]) + { + RenderParallax(FetchForegroundParallaxLayer(file, 1), 4); + res_man.Res_close(this_screen.background_layer_id); // release the screen resource before cacheing the sprites + Send_fore_par1_frames(); + } + else + res_man.Res_close(this_screen.background_layer_id); // release the screen resource + + //---------------------------------------------------- + // walkgrid, mouse & player markers & mouse area rectangle + + Draw_debug_graphics(); // JAMES (08apr97) + + //---------------------------------------------------- + // text blocks + + Print_text_blocs(); // speech blocks and headup debug text + + //---------------------------------------------------- + // menu bar & icons + + ProcessMenu(); + + //---------------------------------------------------- + // ready - blit to screen + + CopyScreenBuffer(); + FlipScreens(); + + //---------------------------------------------------- + // update our fps reading + + frameCount += 1; + if (timeGetTime() > cycleTime) + { + fps = frameCount; + frameCount = 0; + cycleTime = timeGetTime()+1000; + } + //---------------------------------------------------- + // check if we've got time to render the screen again this cycle + // (so drivers can smooth out the scrolling in between normal game cycles) + + EndRenderCycle(&end); + + if (end) // if we haven't got time to render again this cycle, drop out of 'render cycle' while-loop + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if the game is being shut down, drop out + break; + + //---------------------------------------------------- + } // END OF RENDER CYCLE + + + } +#ifdef _DEBUG + else if (console_status) + { + spriteInfo.x = 0; + spriteInfo.y = con_y; + spriteInfo.w = con_width; + spriteInfo.h = con_depth; + spriteInfo.scale = 0; + spriteInfo.scaledWidth = 0; + spriteInfo.scaledHeight = 0; + spriteInfo.type = RDSPR_DISPLAYALIGN+RDSPR_NOCOMPRESSION; // no compression! + spriteInfo.blend = 0; + spriteInfo.data = console_sprite->ad; + spriteInfo.colourTable = 0; + + + rv = DrawSprite( &spriteInfo ); + if (rv) + ExitWithReport("Driver Error %.8x (drawing console) [%s line %u]", rv, __FILE__, __LINE__); + + CopyScreenBuffer(); + FlipScreens(); + } + else + { + StartConsole(); + SetPalette(0, 3, pal, RDPAL_INSTANT); //force the palette + Print_to_console("no valid screen?"); + } +#endif // _DEBUG + +} + +//------------------------------------------------------------------------------------ +// +// Fades down and displays a message on the screen for time seconds +// +void DisplayMsg( uint8 *text, int time ) // Chris 15May97 +{ + mem *text_spr; + _frameHeader *frame; + _spriteInfo spriteInfo; + bool done = false; + _palEntry pal[256]; + _palEntry oldPal[256]; + int16 oldY; + int16 oldX; + uint32 rv; // drivers error return value + + warning("DisplayMsg: %s", (char *) text); + + + if (GetFadeStatus() != RDFADE_BLACK) + { + FadeDown((float) 0.75); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(GetFadeStatus()==RDFADE_DOWN); + } + + Set_mouse(NULL); + Set_luggage(0); //tw28Aug + + EraseBackBuffer(); // for hardware rendering + EraseSoftwareScreenBuffer(); // for software rendering + + text_spr = MakeTextSprite( text, 640, 187, speech_font_id ); + + frame = (_frameHeader*) text_spr->ad; + + spriteInfo.x = screenWide/2 - frame->width/2; + if (!time) + spriteInfo.y = screenDeep/2 - frame->height/2 - RDMENU_MENUDEEP; + else + spriteInfo.y = 400 - frame->height; + spriteInfo.w = frame->width; + spriteInfo.h = frame->height; + spriteInfo.scale = 0; + spriteInfo.scaledWidth = 0; + spriteInfo.scaledHeight = 0; + spriteInfo.type = RDSPR_DISPLAYALIGN+RDSPR_NOCOMPRESSION+RDSPR_TRANS; + spriteInfo.blend = 0; + spriteInfo.data = text_spr->ad + sizeof(_frameHeader); + spriteInfo.colourTable = 0; + oldX = spriteInfo.x; + oldY = spriteInfo.y; + + + rv = DrawSprite( &spriteInfo ); + if (rv) + ExitWithReport("Driver Error %.8x (in DisplayMsg) [%s line %u]", rv, __FILE__, __LINE__); + + + + spriteInfo.x = oldX; + spriteInfo.y = oldY; + + memcpy((char *) oldPal, (char *) palCopy, 256*sizeof(_palEntry)); + + memset(pal, 0, 256*sizeof(_palEntry)); + pal[187].red = 255; + pal[187].green = 255; + pal[187].blue = 255; + SetPalette(0, 256, (uint8 *) pal, RDPAL_FADE); + + CopyScreenBuffer(); + FlipScreens(); + + FadeUp((float)0.75); + + Free_mem(text_spr); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(GetFadeStatus()==RDFADE_UP); + + DWORD targetTime = timeGetTime() + (time*1000); + + while(timeGetTime() < targetTime) + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + + EraseBackBuffer(); // for hardware rendering + EraseSoftwareScreenBuffer(); // for software rendering + + rv = DrawSprite( &spriteInfo ); // Keep the message there even when the user task swaps. + if (rv) + ExitWithReport("Driver Error %.8x (in DisplayMsg) [%s line %u]", rv, __FILE__, __LINE__); + + spriteInfo.y = oldY; // Drivers change the y co-ordinate, don't know why... + spriteInfo.x = oldX; + CopyScreenBuffer(); + FlipScreens(); + } + + SetPalette(0, 256, (uint8 *) oldPal, RDPAL_FADE); +} + +//------------------------------------------------------------------------------------ +// +// Fades message down and removes it, fading up again afterwards +// +void RemoveMsg( void ) // Chris 15May97 +{ + FadeDown((float)0.75); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(GetFadeStatus()==RDFADE_DOWN); + + EraseBackBuffer(); // for hardware rendering + EraseSoftwareScreenBuffer(); // for software rendering + CopyScreenBuffer(); + FlipScreens(); + +// FadeUp((float)0.75); +// removed by JEL (08oct97) to prevent "eye" smacker corruption when restarting game from CD2 +// and also to prevent palette flicker when restoring game to a different CD +// - since the "insert CD" message uses this routine to clean up! + +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void Send_back_par0_frames(void) //James23Jan97 +{ + uint32 j; + + for (j=0; j<cur_bgp0; j++) // could be none at all - theoretically at least + { + Process_image(&bgp0_list[j]); // frame attached to 1st background parallax + } +} +//------------------------------------------------------------------------------------ +void Send_back_par1_frames(void) //James23Jan97 +{ + uint32 j; + + for (j=0; j<cur_bgp1; j++) // could be none at all - theoretically at least + { + Process_image(&bgp1_list[j]); // frame attached to 1nd background parallax + } +} +//------------------------------------------------------------------------------------ +void Send_back_frames(void) //Tony23Sept96 +{ + uint32 j; + + for (j=0; j<cur_back; j++) // could be none at all - theoretically at least + { + Process_image(&back_list[j]); + } +} +//------------------------------------------------------------------------------------ +void Send_sort_frames(void) //Tony23Sept96 +{ + //send the sort frames for printing - layers, shrinkers & normal flat sprites + uint32 j; + + for (j=0; j<cur_sort; j++) // could be none at all - theoretically at least + { + if (sort_list[sort_order[j]].layer_number) //its a layer - minus 1 for true layer number + Process_layer(sort_list[sort_order[j]].layer_number-1); //we need to know from the buildit because the layers will have been sorted in random order + else + Process_image(&sort_list[sort_order[j]]); // sprite + } +} +//------------------------------------------------------------------------------------ +void Send_fore_frames(void) //Tony23Sept96 +{ + uint32 j; + + for (j=0; j<cur_fore; j++) // could be none at all - theoretically at least + { + Process_image(&fore_list[j]); + } +} +//------------------------------------------------------------------------------------ +void Send_fore_par0_frames(void) //James23Jan97 +{ + uint32 j; + + for (j=0; j<cur_fgp0; j++) // could be none at all - theoretically at least + { + Process_image(&fgp0_list[j]); // frame attached to 1st foreground parallax + } +} +//------------------------------------------------------------------------------------ +void Send_fore_par1_frames(void) //James23Jan97 +{ + uint32 j; + + for (j=0; j<cur_fgp1; j++) // could be none at all - theoretically at least + { + Process_image(&fgp1_list[j]); // frame attached to 2nd foreground parallax + } +} +//------------------------------------------------------------------------------------ +void Process_layer(uint32 layer_number) //Tony24Sept96 +{ + uint8 *file; + _layerHeader *layer_head; + _spriteInfo spriteInfo; + uint32 rv; + + #ifdef _DEBUG + uint32 current_layer_area=0; + #endif + + + file = res_man.Res_open(this_screen.background_layer_id); // file points to 1st byte in the layer file + + layer_head = FetchLayerHeader(file,layer_number); // point to layer header + + + spriteInfo.x = layer_head->x; + spriteInfo.y = layer_head->y; + spriteInfo.w = layer_head->width; + spriteInfo.scale = 0; + spriteInfo.scaledWidth = 0; + spriteInfo.scaledHeight = 0; + spriteInfo.h = layer_head->height; + spriteInfo.type = RDSPR_TRANS + RDSPR_RLE256FAST; + spriteInfo.blend = 0; + spriteInfo.data = file+sizeof(_standardHeader)+layer_head->offset; + spriteInfo.colourTable = 0; + + + //------------------------------------------ + // check for largest layer for debug info + #ifdef _DEBUG + current_layer_area = layer_head->width * layer_head->height; + + if (current_layer_area > largest_layer_area) + { + largest_layer_area = current_layer_area; + sprintf (largest_layer_info, "largest layer: %s layer(%d) is %dx%d", FetchObjectName(this_screen.background_layer_id), layer_number, layer_head->width, layer_head->height); + } + #endif + //------------------------------------------ + + rv = DrawSprite( &spriteInfo ); + if (rv) + ExitWithReport("Driver Error %.8x in Process_layer(%d) [%s line %u]", rv, layer_number, __FILE__, __LINE__); + + + res_man.Res_close(this_screen.background_layer_id); + +} +//------------------------------------------------------------------------------------ +void Process_image(buildit *build_unit) // (5nov96 JEL) +{ + uint8 *file, *colTablePtr=NULL; + _animHeader *anim_head; + _frameHeader *frame_head; + _cdtEntry *cdt_entry; + _spriteInfo spriteInfo; + uint32 spriteType; + uint32 rv; + + #ifdef _DEBUG + uint32 current_sprite_area=0; + #endif + + file = res_man.Res_open(build_unit->anim_resource); // open anim resource file & point to base + + anim_head = FetchAnimHeader( file ); + cdt_entry = FetchCdtEntry( file, build_unit->anim_pc ); + frame_head = FetchFrameHeader( file, build_unit->anim_pc ); + + + spriteType = RDSPR_TRANS; // so that 0-colour is transparent + + if (anim_head->blend) + spriteType += RDSPR_BLEND; + + if ((cdt_entry->frameType) & FRAME_FLIPPED) // if the frame is to be flipped (only really applicable to frames using offsets) + spriteType += RDSPR_FLIP; + + if ((cdt_entry->frameType) & FRAME_256_FAST) + { + if ((build_unit->scale)||(anim_head->blend)||(build_unit->shadingFlag)) // scaling, shading & blending don't work with RLE256FAST + spriteType += RDSPR_RLE256; // but the same compression can be decompressed using the RLE256 routines! + else + spriteType += RDSPR_RLE256FAST; + } + else + { + switch (anim_head->runTimeComp) // what compression was used? + { + case NONE: + spriteType += RDSPR_NOCOMPRESSION; + break; + case RLE256: + spriteType += RDSPR_RLE256; + break; + case RLE16: + spriteType += RDSPR_RLE16; + colTablePtr = (uint8*)(anim_head+1) + anim_head->noAnimFrames*sizeof(_cdtEntry); + // points to just after last cdt_entry, ie. start of colour table + break; + } + } + + if (build_unit->shadingFlag==1) // if we want this frame to be affected by the shading mask + spriteType += RDSPR_SHADOW; // add the status bit + + spriteInfo.x = build_unit->x; + spriteInfo.y = build_unit->y; + spriteInfo.w = frame_head->width; + spriteInfo.h = frame_head->height; + spriteInfo.scale = build_unit->scale; + spriteInfo.scaledWidth = build_unit->scaled_width; + spriteInfo.scaledHeight = build_unit->scaled_height; + spriteInfo.type = spriteType; + spriteInfo.blend = anim_head->blend; + spriteInfo.data = (uint8*)(frame_head+1); // points to just after frame header, ie. start of sprite data + spriteInfo.colourTable = colTablePtr; + + + //------------------------------------------ + // check for largest layer for debug info + #ifdef _DEBUG + current_sprite_area = frame_head->width * frame_head->height; + + if (current_sprite_area > largest_sprite_area) + { + largest_sprite_area = current_sprite_area; + sprintf (largest_sprite_info, "largest sprite: %s frame(%d) is %dx%d", FetchObjectName(build_unit->anim_resource), build_unit->anim_pc, frame_head->width, frame_head->height); + } + #endif + //------------------------------------------ + + + //----------------------------------------------------------- + #ifdef _DEBUG + if (SYSTEM_TESTING_ANIMS) // see anims.cpp + { + if ((spriteInfo.x + spriteInfo.scaledWidth) >= 639) // bring the anim into the visible screen + spriteInfo.x = 639-spriteInfo.scaledWidth; // but leave extra pixel at edge for box + + if ((spriteInfo.y + spriteInfo.scaledHeight) >= 399) + spriteInfo.y = 399-spriteInfo.scaledHeight; + + if (spriteInfo.x < 1) + spriteInfo.x = 1; + + if (spriteInfo.y < 1) + spriteInfo.y = 1; + + rect_x1 = spriteInfo.x - 1; // create box to surround sprite - just outside sprite box + rect_y1 = spriteInfo.y - 1; + rect_x2 = spriteInfo.x + spriteInfo.scaledWidth; + rect_y2 = spriteInfo.y + spriteInfo.scaledHeight; + } + #endif + //----------------------------------------------------------- + + //-------------------------------------------------- +// #ifdef _DEBUG +// if (frame_head->width <= 1) +// { +// Zdebug(8,"WARNING: 1-pixel-wide frame found in %s (%d)", FetchObjectName(build_unit->anim_resource), build_unit->anim_resource); +// } +// #endif + //-------------------------------------------------- + + rv = DrawSprite( &spriteInfo ); + if (rv) + ExitWithReport("Driver Error %.8x with sprite %s (%d) in Process_image [%s line %u]", rv, FetchObjectName(build_unit->anim_resource), build_unit->anim_resource, __FILE__, __LINE__); + + + res_man.Res_close(build_unit->anim_resource); // release the anim resource + +} +//------------------------------------------------------------------------------------ +void Reset_render_lists(void) //Tony18Sept96 +{ +//reset the sort lists - do this before a logic loop +//takes into account the fact that the start of the list is pre-built with the special sortable layers + + uint32 j; + + cur_bgp0=0; + cur_bgp1=0; + cur_back=0; + cur_sort=this_screen.number_of_layers; //beginning of sort list is setup with the special sort layers + cur_fore=0; + cur_fgp0=0; + cur_fgp1=0; + + + if (cur_sort) //there are some layers - so rebuild the sort order positioning + for (j=0;j<cur_sort;j++) + sort_order[j]=j; //rebuild the order list +} +//------------------------------------------------------------------------------------ +void Sort_the_sort_list(void) //Tony18Sept96 +{ +//sort the list + + uint16 i,j,swap; + + +//sort the list + + if (cur_sort>1) //cannot bubble sort 0 or 1 items! + for (i=0; i<cur_sort-1; i++) + for (j=0; j<cur_sort-1; j++) + if (sort_list[sort_order[j]].sort_y > sort_list[sort_order[j+1]].sort_y) //this > next then swap + { swap=sort_order[j]; + sort_order[j]=sort_order[j+1]; + sort_order[j+1]=swap; + } +} +//------------------------------------------------------------------------------------ +void Register_frame(int32 *params, buildit *build_unit) // (1nov96JEL) +{ + // params: 0 pointer to mouse structure or NULL for no write to mouse list (non-zero means write sprite-shape to mouse list) + // 1 pointer to graphic structure + // 2 pointer to mega structure + + Object_mega *ob_mega; + Object_graphic *ob_graph; + Object_mouse *ob_mouse; + uint8 *file; + _frameHeader *frame_head; + _animHeader *anim_head; + _cdtEntry *cdt_entry; + int scale=0; + + + //------------------------------------------- + // open animation file & set up the necessary pointers + + ob_graph = (Object_graphic *) params[1]; + + #ifdef _DEBUG + if (ob_graph->anim_resource == 0) + Con_fatal_error("ERROR: %s(%d) has no anim resource in Register_frame [line=%d file=%s]", FetchObjectName(ID), ID, __LINE__, __FILE__); + #endif + + file = res_man.Res_open(ob_graph->anim_resource); + + anim_head = FetchAnimHeader( file ); + cdt_entry = FetchCdtEntry( file, ob_graph->anim_pc ); + frame_head = FetchFrameHeader( file, ob_graph->anim_pc ); + + + #ifdef _DEBUG + if (ID == CUR_PLAYER_ID) // update player graphic details for on-screen debug info + { + playerGraphic.type = ob_graph->type; + playerGraphic.anim_resource = ob_graph->anim_resource; + playerGraphic.anim_pc = ob_graph->anim_pc+1; // counting 1st frame as 'frame 1' + player_graphic_no_frames = anim_head->noAnimFrames; + } + #endif + + //------------------------------------------- + // fill in the buildit structure for this frame + + build_unit->anim_resource = ob_graph->anim_resource; //retrieve the resource + build_unit->anim_pc = ob_graph->anim_pc; //retrieve the frame + build_unit->layer_number = 0; //not a layer + + if (ob_graph->type & SHADED_SPRITE) + build_unit->shadingFlag = 1; // affected by shading mask + else + build_unit->shadingFlag = 0; // not shaded + + //------------------------------------------- + // check if this frame has offsets ie. this is a scalable mega frame + if ((cdt_entry->frameType) & FRAME_OFFSET) + { + ob_mega = (Object_mega *) params[2]; // param 2 is pointer to mega structure + + // calc scale at which to print the sprite, based on feet y-coord & scaling constants (NB. 'scale' is actually 256*true_scale, to maintain accuracy) + scale = (ob_mega->scale_a * ob_mega->feet_y + ob_mega->scale_b)/256; // Ay+B gives 256*scale ie. 256*256*true_scale for even better accuracy, ie. scale = (Ay+B)/256 + // calc final render coordinates (top-left of sprite), based on feet coords & scaled offsets + build_unit->x = ob_mega->feet_x + (cdt_entry->x * scale)/256; // add scaled offsets to feet coords + build_unit->y = ob_mega->feet_y + (cdt_entry->y * scale)/256; + + // work out new width and height + build_unit->scaled_width = ((scale * frame_head->width) / 256); // always divide by 256 after everything else, to maintain accurary + build_unit->scaled_height = ((scale * frame_head->height) / 256); + } + else // it's a non-scaling anim + { + // get render coords for sprite, from cdt + build_unit->x = cdt_entry->x; //retrieve the x + build_unit->y = cdt_entry->y; //retrieve the y + + // get width and height + build_unit->scaled_width = frame_head->width; + build_unit->scaled_height = frame_head->height; + } + //------------------------------------------- + + build_unit->scale = scale; // either 0 or required scale, depending on whether 'scale' computed + + // calc the bottom y-coord for sorting purposes + build_unit->sort_y = build_unit->y + build_unit->scaled_height - 1; + //------------------------------------------- + + if (params[0]) // passed a mouse structure, so add to the mouse_list + { + ob_mouse = (Object_mouse *) params[0]; + + if (ob_mouse->pointer) // only if 'pointer' isn't NULL (James13feb97) + { + #ifdef _DEBUG + if (cur_mouse==TOTAL_mouse_list) + Con_fatal_error("ERROR: mouse_list full [%s line %u]",__FILE__,__LINE__); + #endif + + mouse_list[cur_mouse].x1 = build_unit->x; + mouse_list[cur_mouse].y1 = build_unit->y; + mouse_list[cur_mouse].x2 = build_unit->x + build_unit->scaled_width; + mouse_list[cur_mouse].y2 = build_unit->y + build_unit->scaled_height; + + mouse_list[cur_mouse].priority = ob_mouse->priority; + mouse_list[cur_mouse].pointer = ob_mouse->pointer; + + //----------------------------------------------- + // (James17jun97) + // check if pointer text field is set due to previous object using this slot (ie. not correct for this one) + if ((mouse_list[cur_mouse].pointer_text) && (mouse_list[cur_mouse].id != (int32)ID)) // if 'pointer_text' field is set, but the 'id' field isn't same is current id + mouse_list[cur_mouse].pointer_text=0; // then we don't want this "left over" pointer text + //----------------------------------------------- + + + mouse_list[cur_mouse].id = ID; + + mouse_list[cur_mouse].anim_resource = 0; // not using sprite as detection mask + mouse_list[cur_mouse].anim_pc = 0; + + cur_mouse++; + } + } + //------------------------------------------- + + res_man.Res_close(ob_graph->anim_resource); // close animation file +} +//------------------------------------------------------------------------------------ + +int32 FN_register_frame(int32 *params) // (27nov96 JEL) +{ + //this call would be made from an objects service script 0 + + // params: 0 pointer to mouse structure or NULL for no write to mouse list (non-zero means write sprite-shape to mouse list) + // 1 pointer to graphic structure + // 2 pointer to mega structure or NULL if not a mega + + Object_graphic *ob_graph = (Object_graphic *) params[1]; + + + switch (ob_graph->type & 0x0000ffff) // check low word for sprite type + { + //--------------- + case BGP0_SPRITE: + { + #ifdef _DEBUG + if (cur_bgp0==MAX_bgp0_sprites) + Con_fatal_error("ERROR: bgp0_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + Register_frame(params, &bgp0_list[cur_bgp0]); + cur_bgp0++; + } + break; + //--------------- + case BGP1_SPRITE: + { + #ifdef _DEBUG + if (cur_bgp1==MAX_bgp1_sprites) + Con_fatal_error("ERROR: bgp1_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + Register_frame(params, &bgp1_list[cur_bgp1]); + cur_bgp1++; + } + break; + //--------------- + case BACK_SPRITE: + { + #ifdef _DEBUG + if (cur_back==MAX_back_sprites) + Con_fatal_error("ERROR: back_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + Register_frame(params, &back_list[cur_back]); + cur_back++; + } + break; + //--------------- + case SORT_SPRITE: + { + #ifdef _DEBUG + if (cur_sort==MAX_sort_sprites) + Con_fatal_error("ERROR: sort_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + sort_order[cur_sort]=cur_sort; + + Register_frame(params, &sort_list[cur_sort]); + cur_sort++; + } + break; + //--------------- + case FORE_SPRITE: + { + #ifdef _DEBUG + if (cur_fore==MAX_fore_sprites) + Con_fatal_error("ERROR: fore_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + Register_frame(params, &fore_list[cur_fore]); + cur_fore++; + } + break; + //--------------- + case FGP0_SPRITE: + { + #ifdef _DEBUG + if (cur_fgp0==MAX_fgp0_sprites) + Con_fatal_error("ERROR: fgp0_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + Register_frame(params, &fgp0_list[cur_fgp0]); + cur_fgp0++; + } + break; + //--------------- + case FGP1_SPRITE: + { + #ifdef _DEBUG + if (cur_fgp1==MAX_fgp1_sprites) + Con_fatal_error("ERROR: fgp1_list full in FN_register_frame [line=%d file=%s]",__LINE__,__FILE__); + #endif + + Register_frame(params, &fgp1_list[cur_fgp1]); + cur_fgp1++; + } + break; + //--------------- + // NO_SPRITE no registering! + } + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +void Start_new_palette(void) //Tony25Sept96 +{ + //start layer palette fading up + uint8 black[4]={0,0,0,0}; + + uint8 *screenFile; + + //if the screen is still fading down then wait for black - could happen when everythings cached into a large memory model + do + { + ServiceWindows(); + } + while(GetFadeStatus()==RDFADE_DOWN); + + + screenFile = res_man.Res_open(this_screen.background_layer_id); // open the screen file + + UpdatePaletteMatchTable((uint8 *) FetchPaletteMatchTable(screenFile)); + + SetPalette(0, 256, FetchPalette(screenFile), RDPAL_FADE); + lastPaletteRes=0; // indicating that it's a screen palette + + res_man.Res_close(this_screen.background_layer_id); // close screen file + + //FadeUp((float)1.75); // start fade up + FadeUp((float)0.75); // start fade up + + this_screen.new_palette=0; // reset +} +//------------------------------------------------------------------------------------ +int32 FN_update_player_stats(int32 *params) //Tony28Nov96 +{ +//engine needs to know certain info about the player + + + Object_mega *ob_mega = (Object_mega *) params[0]; + + + + + this_screen.player_feet_x = ob_mega->feet_x; + this_screen.player_feet_y = ob_mega->feet_y; + + PLAYER_FEET_X=ob_mega->feet_x; //for the script + PLAYER_FEET_Y=ob_mega->feet_y; + PLAYER_CUR_DIR=ob_mega->current_dir; + + SCROLL_OFFSET_X=this_screen.scroll_offset_x; + + //Zdebug(42,"%d %d", ob_mega->feet_x, ob_mega->feet_y); + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_fade_down(int32 *params) //Tony5Dec96 +{ + + if (GetFadeStatus()==RDFADE_NONE) //NONE means up! can only be called when screen is fully faded up - multiple calls wont have strange effects + { + FadeDown((float)0.75); + + return(IR_CONT); + } + + return(IR_CONT); +} + +int32 FN_fade_up(int32 *params) //Chris 15May97 +{ + do + { + ServiceWindows(); + } + while(GetFadeStatus()==RDFADE_DOWN); + + if (GetFadeStatus()==RDFADE_BLACK) + { + FadeUp((float)0.75); + + return(IR_CONT); + } + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// typedef struct +// { +// uint8 red; +// uint8 green; +// uint8 blue; +// uint8 alpha; +// } _palEntry; + +//------------------------------------------------------------ +// typedef struct +// { +// uint8 firstEntry; // first colour number in this palette (0..255) +// uint8 noEntries; // number of Entries-1 (0..255) to be taken as (1..256) +// } _paletteHeader; + +//------------------------------------------------------------ +int32 FN_set_palette(int32 *params) // James05jun97 +{ + SetFullPalette(params[0]); + + return(IR_CONT); +} +//------------------------------------------------------------ +void SetFullPalette(int32 palRes) // James17jun97 +{ + // params 0 resource number of palette file + // or 0 if it's to be the palette from the current screen + + uint8 black[4]={0,0,0,0}; + uint8 *file; + _standardHeader *head; + + + //---------------------------------- + // fudge for hut interior + // - unpausing should restore last palette as normal (could be screen palette or 'dark_palette_13') + // - but restoring the screen palette after 'dark_plaette_13' should now work properly too! + if (LOCATION==13) // hut interior + { + if (palRes==0xffffffff) // unpausing + palRes = lastPaletteRes; // restore whatever palette was last set (screen palette or 'dark_palette_13') + } + else + { + // (James 03sep97) + // check if we're just restoring the current screen palette + // because we might actually need to use a separate palette file anyway + // eg. for pausing & unpausing during the eclipse + + if (palRes==0xffffffff) // unpausing (fudged for location 13) + palRes=0; // we really meant '0' + + if ((palRes==0) && (lastPaletteRes)) + palRes = lastPaletteRes; + } + //---------------------------------- + + + if (palRes) // non-zero: set palette to this separate palette file + { + head = (_standardHeader*)res_man.Res_open(palRes); // open the palette file + + #ifdef _DEBUG + if (head->fileType != PALETTE_FILE) + Con_fatal_error("FN_set_palette() called with invalid resource! (%s line %u)",__FILE__,__LINE__); + #endif + + file = (uint8*)(head+1); + + file[0] = 0; // always set colour 0 to black + file[1] = 0; // because most background screen palettes have a bright colour 0 + file[2] = 0; // although it should come out as black in the game! + file[3] = 0; + +// UpdatePaletteMatchTable(file+(256*4)); // not yet in separate palette files + + SetPalette(0, 256, file, RDPAL_INSTANT); + + if (palRes != CONTROL_PANEL_PALETTE) // (James 03sep97) + lastPaletteRes=palRes; // indicating that it's a separate palette resource + + + res_man.Res_close(palRes); // close palette file + } + else // 0: set palette to current screen palette + { + if (this_screen.background_layer_id) + { + file = res_man.Res_open(this_screen.background_layer_id); // open the screen file + + UpdatePaletteMatchTable((uint8 *) FetchPaletteMatchTable(file)); + + SetPalette(0, 256, FetchPalette(file), RDPAL_INSTANT); + lastPaletteRes=0; // indicating that it's a screen palette + + res_man.Res_close(this_screen.background_layer_id); // close screen file + } + else + Con_fatal_error("FN_set_palette(0) called, but no current screen available! (%s line %u)",__FILE__,__LINE__); + } +} +//------------------------------------------------------------ + +int32 FN_restore_game(int32 *params) +{ + + return (IR_CONT); +} + +//------------------------------------------------------------ + +int32 FN_change_shadows(int *params) +{ + uint32 rv; + + if (this_screen.mask_flag) // if last screen was using a shading mask (see below) (James 08apr97) + { + rv = CloseLightMask(); + + if (rv) + ExitWithReport("Driver Error %.8x [%s line %u]", rv, __FILE__, __LINE__); + + this_screen.mask_flag = 0; + } + + return (IR_CONT); +} + + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + diff --git a/sword2/build_display.h b/sword2/build_display.h new file mode 100644 index 0000000000..84fda47aab --- /dev/null +++ b/sword2/build_display.h @@ -0,0 +1,81 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _BUILD_DISPLAY +#define _BUILD_DISPLAY + +#include "driver/driver96.h" + + +typedef struct //structure filled out by each object to register its graphic printing requrements +{ + int16 x; + int16 y; + uint16 scaled_width; + uint16 scaled_height; + int16 sort_y; + uint32 anim_resource; + uint16 anim_pc; + uint16 scale; //denotes a scaling sprite at print time - and holds the scaling value for the shrink routine + uint16 layer_number; //non zero means this item is a layer - retrieve from background layer and send to special renderer + uint8 shadingFlag; // non zero means we want this frame to be affected by the shading mask + +// uint32 write_mouse_list; //if none zero the shrinker should write coordinates to this mouse_list number + +} buildit; + +// declared externally so that debug.cpp can display these in the info +#define MAX_bgp0_sprites 6 +#define MAX_bgp1_sprites 6 +#define MAX_back_sprites 30 +#define MAX_sort_sprites 30 +#define MAX_fore_sprites 30 +#define MAX_fgp0_sprites 6 +#define MAX_fgp1_sprites 6 + +// declared externally so that debug.cpp can display these in the info +extern uint32 cur_bgp0; +extern uint32 cur_bgp1; +extern uint32 cur_back; +extern uint32 cur_sort; +extern uint32 cur_fore; +extern uint32 cur_fgp0; +extern uint32 cur_fgp1; + +#ifdef _DEBUG +extern char largest_layer_info[128]; +extern char largest_sprite_info[128]; +#endif + +// the only build list needed externally - by layers.cpp - for adding layers to sort list +extern buildit sort_list[]; + +// function prototypes needed externally +void Reset_render_lists(void); +void Build_display(void); //Tony21Sept96 +int32 FN_fade_down(int32 *params); //Tony5Dec96 +int32 FN_fade_up(int32 *params); // Chris 15May97 +void Process_image(buildit *build_unit); // (5nov96 JEL) +void DisplayMsg( uint8 *text, int time ); // (Chris 15May97) +void RemoveMsg(void); +void SetFullPalette(int32 palRes); // James17jun97 + +extern uint32 fps; // needed by debug.cpp for displaying as part of top-screen info + +#endif diff --git a/sword2/console.cpp b/sword2/console.cpp new file mode 100644 index 0000000000..3ae37ae23a --- /dev/null +++ b/sword2/console.cpp @@ -0,0 +1,1341 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include "driver/driver96.h" + +uint32 console_status=0; //0 off // LEFT IN RELEASE VERSION + + +#ifdef _DEBUG + +//----------------------------------------------------------------------------------------------------------------------- +//its the console! <great> +//----------------------------------------------------------------------------------------------------------------------- +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> // for version string stuff + +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "events.h" // so we can disaply the event list in Con_display_events() +#include "header.h" +#include "layers.h" +#include "logic.h" +#include "maketext.h" // for InitialiseFontResourceFlags() +#include "mouse.h" +#include "mem_view.h" +#include "memory.h" +#include "protocol.h" +#include "resman.h" +#include "save_rest.h" +#include "startup.h" +#include "sword2.h" +#include "time.h" + +//----------------------------------------------------------------------------------------------------------------------- +// local function prototypes + +uint32 Parse_user_input(void); // Tony13Aug96 +void Clear_console_line(void); // Tony13Aug96 +void Con_help(void); // Tony13Aug96 + +void Con_colour_block(int x, int width, int height, uint32 pen, uint32 paper, uint8 *sprite_data_ad); +void Con_print(uint8 *ascii, uint32 pen, uint32 paper); +uint32 Tconsole(uint32 mode); // Tony9Oct96 + +void Con_list_savegames(void); // James05feb97 +void Con_save_game(int total_commands, uint8 *slotString, uint8 *description); // James05feb97 +void Con_restore_game(int total_commands, uint8 *slotString); // James05feb97 +uint8 Is_number(uint8 *ascii); // James05feb97 +void Con_start_timer(int total_commands, uint8 *slotString); // Paul12feb97 +void ShowVar(uint8 *varNoPtr); // James19mar97 +void HideVar(uint8 *varNoPtr); // James19mar97 +void Con_display_version(void); // James27mar97 + +void Var_check(uint8 *pointer); //Tony8Jan97 +void Var_set(uint8 *pointer, uint8 *p2); //Tony8Jan97 + +void Con_display_events(); // (James11july97) + + +//----------------------------------------------------------------------------------------------------------------------- +uint8 wantSfxDebug=0; // sfx debug file enabled/disabled from console +//----------------------------------------------------------------------------------------------------------------------- + + +#define MAX_CONSOLE_BUFFER 70 +#define MAX_CONSOLE_PARAMS 5 + +#define CON_PEN 187 + +#define VK_TAB 0x09 +#define VK_RETURN 0x0D + +char console_buffer[MAX_CONSOLE_BUFFER]; + +char last_command[MAX_CONSOLE_BUFFER]; // James 03apr97 +int last_command_len=0; // James 03apr97 + +uint8 grabbingSequences=0; + +int console_pos=0; //cursor position within the typed line + +int console_mode=0; //0 is the base command line + //1 means only parse for yes or no commands + //1 on + + + + +#define TOTAL_CONSOLE_COMMANDS 47 + +uint8 commands[TOTAL_CONSOLE_COMMANDS][9]= // note '9' is max command length including null-terminator +{ + "HELP", // 0 + "MEM", // 1 + "Q", // 2 + "TONY", // 3 + "YES", // 4 + "NO", // 5 + "RES", // 6 + "STARTS", // 7 + "START", // 8 + "INFO", // 9 + "WALKGRID", // 10 + "MOUSE", // 11 + "PLAYER", // 12 + "RESLOOK", // 13 + "CUR", // 14 + "RUNLIST", // 15 + "KILL", // 16 + "NUKE", // 17 + "S", // 18 + "VAR", // 19 + "RECT", // 20 + "CLEAR", // 21 + "DEBUGON", // 22 + "DEBUGOFF", // 23 + "SAVEREST", // 24 + "SAVES", // 25 + "SAVE", // 26 + "RESTORE", // 27 + "BLTFXON", // 28 + "BLTFXOFF", // 29 + "TIMEON", // 30 + "TIMEOFF", // 31 + "TEXT", // 32 + "SHOWVAR", // 33 + "HIDEVAR", // 34 + "VERSION", // 35 + "SOFT", // 36 + "HARD", // 37 + "ANIMTEST", // 38 + "TEXTTEST", // 39 + "LINETEST", // 40 + "GRAB", // 41 + "EVENTS", // 42 + "SFX", // 43 + "ENGLISH", // 44 + "FINNISH", // 45 + "POLISH" // 46 +}; + +mem *console_sprite; +uint32 con_y; +uint32 con_depth; +uint32 con_width; +uint32 con_chr_height; + +#define CON_lines 20 //10 lines deep + +//----------------------------------------------------------------------------------------------------------------------- +void Init_console(void) //Tony9Sept96 +{ +//grab the memory for the console sprite + + uint32 j; + uint8 *ad; + uint8 white[4] = {255,255,255,0}; // Chris 11Apr97 + + + con_chr_height=12; + con_width=screenWide; //max across + + SetPalette(CON_PEN, 1, white, RDPAL_INSTANT); // Force a palatte for the console. Chris 11Apr97 + + console_sprite = Twalloc(con_width*(CON_lines*con_chr_height), MEM_float, UID_con_sprite); + + con_depth= CON_lines*con_chr_height; + con_y= 399-con_depth; + + + +//clear the buffer for a nice fresh start + ad=console_sprite->ad; + for (j=0;j<con_width*(CON_lines*con_chr_height);j++) + *(ad+j)=0; + + + if (!console_sprite) + { + Zdebug("Init_console Talloc fail"); + ExitWithReport("Init_console Talloc fail [file=%s line=%u]",__FILE__,__LINE__); + } + + Zdebug("console height %d, y %d", con_depth, con_y); + + //first time in message + Con_display_version(); +} +//----------------------------------------------------------------------------------------------------------------------- +void StartConsole(void) //Tony12Aug96 +{ +//start console up and restart new line +//can ne called for newline + + int j; + + + console_pos=0; //start of new line + + for (j=0;j<MAX_CONSOLE_BUFFER;j++) //we need to clear the whole buffer - else the cursor overwrites the end 0 + console_buffer[j]=0; + + + console_status=1; //on +} +//----------------------------------------------------------------------------------------------------------------------- +void EndConsole(void) //Tony9Oct96 +{ + + console_status=0; //off +} +//----------------------------------------------------------------------------------------------------------------------- +uint32 Tconsole(uint32 mode) //Tony9Oct96 +{ +//call from anywhere +//returns a positive value of the token typed or 0 for windows quiting - the caller should drop back + + uint32 ret,breakOut=0; + + + + + + + + console_mode=mode; //set command frame + + + StartConsole(); + + while (TRUE) + { + if (ServiceWindows() == RDERR_APPCLOSED) + { + break; + } + + while (!gotTheFocus) + { + if (ServiceWindows() == RDERR_APPCLOSED) + { + breakOut = 1; + break; + } + + } + if (breakOut) + { + break; + } + +//----- + if (ret = One_console()) + { EndConsole(); + return(ret); + } +//----- + Build_display(); //create and flip the screen + } + + + +//a windows message is throwing us out of here + + EndConsole(); //switch off drawing + return(0); +} +//----------------------------------------------------------------------------------------------------------------------- +void Scroll_console(void) //Tony13Aug96 +{ +//scroll the console sprite up 12 pixels + + uint32 *to_buffer; + uint32 *from_buffer; + int x; + + + x=((con_depth-con_chr_height)*640)/4; //number of dwords + + to_buffer= (uint32 *) console_sprite->ad; //base of console sprite + from_buffer= to_buffer+((con_chr_height*640)/4); + + while(x--) + *(to_buffer++)=*(from_buffer++); + + + + Clear_console_line(); //blank the on-coming bottom line +} +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +void Clear_console_line(void) //Tony13Aug96 +{ +//blank the bottom line + + uint32 *pbuffer; + uint32 x; + + + pbuffer= (uint32 *) console_sprite->ad; //base of our off-screen back buffer + pbuffer+= ((con_depth-con_chr_height)*con_width/4); //index to console text position + + for (x=0;x<con_chr_height*(con_width/4);x++) //clear the bottom text line + *(pbuffer+x)=0; +} +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +void Print_to_console(char *format,...) //Tony13Aug96 +{ +//print a NULL terminated string of ascii to the next console line +//we can assume that the user has just entered a command by pressing return - which means we're on a clean line +//so output the line and line feed + + va_list arg_ptr; // Variable argument pointer + char buf[150]; + + va_start(arg_ptr,format); + _vsnprintf( buf, 150, format, arg_ptr); + + Con_print( (uint8*)buf, 2, 0); + Scroll_console(); //line feed + +} +//----------------------------------------------------------------------------------------------------------------------- +void Temp_print_to_console(char *format,...) //Tony13Aug96 +{ +//print a NULL terminated string of ascii to the next console line +//we can assume that the user has just entered a command by pressing return - which means we're on a clean line +//so output the line and line feed + + va_list arg_ptr; // Variable argument pointer + char buf[150]; + + va_start(arg_ptr,format); + _vsnprintf( buf, 150, format, arg_ptr); + + Con_print( (uint8*)buf, 2, 0); + +} +//----------------------------------------------------------------------------------------------------------------------- +uint32 One_console(void) //Tony12Aug96 +{ +//its the console command line system +//do an update - check keys and flash cursor and so on + char c; + static int flash=0; //controls the flashing cursor rate + uint32 res; + + + if (KeyWaiting()) + { + ReadKey(&c); + + + if (!c) //escape sequences + { + } + else if (c==VK_TAB) // UP arrow + { + if (last_command_len) // if anything stored in buffer + { + // retrieve 'last_command' buffer + memset (console_buffer, 0, MAX_CONSOLE_BUFFER); // first clear the entire current buffer + memcpy (console_buffer, last_command, last_command_len); // now copy in the last command + console_pos = last_command_len; + } + } + else if (c==VK_RETURN) //RETurn + { + console_buffer[console_pos]=' '; //by putting a space in we'll always have a chr$ in the buffer + Clear_console_line(); + Print_to_console(console_buffer); + +// parse the input I guess + + if (console_pos) + { + // save to 'last_command' buffer, in case need to repeat same command (James03apr97) + memcpy (last_command, console_buffer, console_pos); // get a copy of the current command + last_command_len = console_pos; // get a copy of the length as well + + res = Parse_user_input(); + if (res) + return(res); + } + + StartConsole(); //reset buffer + } + else if (c==8) //delete + { + if (console_pos) + { + console_buffer[console_pos]=0; //delete cursor chr$ + console_pos--; + console_buffer[console_pos]=0; + } + } + else if ((c<32)||(c>'z')) + Zdebug("console ignoring key - %d", c); + else + { + if (console_pos<(MAX_CONSOLE_BUFFER-1)) //less one to leave room for the cursor + console_buffer[console_pos++]=c; + + else //end of line has been reached, so keep replacing last letter + console_buffer[console_pos-1]=c; //replace + } + } + + + flash++; + if (flash<7) + console_buffer[console_pos]='_'; + else + console_buffer[console_pos]=' '; //by putting a space in we'll always have a chr$ in the buffer + + if (flash==14) + flash=0; + + + +//update the real screen - done every cycle to keep the cursor flashing + Clear_console_line(); + Con_print( (uint8*)console_buffer, 2, 0); + + return(0); +} +//----------------------------------------------------------------------------------------------------------------------- +uint32 Parse_user_input(void) //Tony13Aug96 +{ +//pressed return and now we need to analyse whats been written and call up the relevent commands + + uint8 input[MAX_CONSOLE_PARAMS][MAX_CONSOLE_BUFFER]; + int i,j,total_commands=0; + int index=0; + uint32 rv; // for driver return value + uint8 pal[4]={255,255,255,0}; + + + + +//quick check for numbers here + if (!isalpha(console_buffer[0])) + { + Print_to_console("Eh?"); //print the standard error message and quit + return(0); + } + + + + j=0; //current command + do + { + i=0; + do + input[j][i++]=toupper(console_buffer[index++]); + while( isgraph(console_buffer[index]) ); + + input[j][i]=0; //NULL terminate + + j++; + total_commands++; + + if (index==console_pos) + break; + + do + index++; + while( console_buffer[index]==' ' ); + } + while(j<MAX_CONSOLE_PARAMS); //only parse first 5 params + + + + +//try to find the first word in the commands base + + for (j=0;j<TOTAL_CONSOLE_COMMANDS;j++) + { + i=0; + while((input[0][i]==commands[j][i])&&(input[0][i])) + i++; + + if ((!input[0][i])&&(!commands[j][i])) //got to the end of an entry - so must have matched the whole word + { + switch(console_mode) //the console mode denotes the scope of the commands accepted 0 is the base mode + { //external console commands may only be requiring a yes/no input for example + //a different scope would only accept yes and no and drop back out when found... see? + + case 0: //base command line + switch(j) + { + //--------------------------------- + case 0: // HELP + Con_help(); + return(0); + break; + //--------------------------------- + case 1: // MEM + Console_mem_display(); + return(0); + break; + //--------------------------------- + case 2: // Q + return(1); //quit the console + break; + //--------------------------------- + case 3: // TONY + Print_to_console("What about him?"); + return(0); + break; + //--------------------------------- + case 6: // RES + res_man.Print_console_clusters(); + return(0); + break; + //--------------------------------- + case 7: // STARTS + Con_print_start_menu(); + return(0); + break; + //--------------------------------- + case 8: // START + Con_start(&input[1][0]); + SetPalette(187, 1, pal, RDPAL_INSTANT); //force the palette + return(0); + break; + //--------------------------------- + case 9: // INFO + displayDebugText = 1-displayDebugText; + if (displayDebugText) + Print_to_console("info text on"); + else + Print_to_console("info text off"); + return(0); + break; + //--------------------------------- + case 10: // WALKGRID + displayWalkGrid = 1-displayWalkGrid; + if (displayWalkGrid) + Print_to_console("walk-grid display on"); + else + Print_to_console("walk-grid display off"); + return(0); + break; + //--------------------------------- + case 11: // MOUSE + displayMouseMarker = 1-displayMouseMarker; + if (displayMouseMarker) + Print_to_console("mouse marker on"); + else + Print_to_console("mouse marker off"); + return(0); + break; + //--------------------------------- + case 12: // PLAYER + displayPlayerMarker = 1-displayPlayerMarker; + if (displayPlayerMarker) + Print_to_console("player feet marker on"); + else + Print_to_console("player feet marker off"); + return(0); + break; + //--------------------------------- + case 13: // RESLOOK + res_man.Examine_res(&input[1][0]); + return(0); + break; + //--------------------------------- + case 14: // CUR + Print_current_info(); + return(0); + break; + //--------------------------------- + case 15: // RUNLIST + LLogic.Examine_run_list(); + return(0); + break; + //--------------------------------- + case 16: // KILL + res_man.Kill_res(&input[1][0]); + return(0); + break; + //--------------------------------- + case 17: // NUKE + Print_to_console("killing all resources except variable file & player object..."); + res_man.Kill_all_res(1); // '1' means we want output to console + return(0); + break; + //--------------------------------- + case 18: // S (same as START) + Con_start(&input[1][0]); + SetPalette(187, 1, pal, RDPAL_INSTANT); //force the palette + return(0); + break; + //--------------------------------- + case 19: // VAR + if (total_commands==2) + Var_check(&input[1][0]); + else Var_set(&input[1][0], &input[2][0]); + + return(0); + break; + //--------------------------------- + case 20: // RECT + definingRectangles = 1-definingRectangles; // switch on/off + if (definingRectangles) + Print_to_console("mouse rectangles enabled"); + else + Print_to_console("mouse rectangles disabled"); + draggingRectangle=0; // reset (see debug.cpp & mouse.cpp) + return(0); + break; + //--------------------------------- + case 21: // CLEAR + Print_to_console("killing all object resources except player..."); + res_man.Kill_all_objects(1); // '1' means we want output to console + return(0); + break; + //--------------------------------- + case 22: // DEBUGON + displayDebugText = 1; + displayWalkGrid = 1; + displayMouseMarker = 1; + displayPlayerMarker = 1; + displayTextNumbers = 1; + + Print_to_console("enabled all on-screen debug info"); + return(0); + break; + //--------------------------------- + case 23: // DEBUGOFF + displayDebugText = 0; + displayWalkGrid = 0; + displayMouseMarker = 0; + displayPlayerMarker = 0; + displayTextNumbers = 0; + + definingRectangles = 0; + draggingRectangle = 0; + + Print_to_console("disabled all on-screen debug info"); + return(0); + break; + //--------------------------------- + case 24: // SAVEREST + testingSnR = 1-testingSnR; + if (testingSnR) + Print_to_console("Enabled S&R logic_script stability checking"); + else + Print_to_console("Disabled S&R logic_script stability checking"); + return(0); + break; + //--------------------------------- + case 25: // SAVES (James05feb97) + Print_to_console("Savegames:"); + Con_list_savegames(); + return(0); + break; + //--------------------------------- + case 26: // SAVE <slotNo> <description> (James05feb97) + Con_save_game(total_commands, &input[1][0], &input[2][0]); + return(0); + break; + //--------------------------------- + case 27: // RESTORE <slotNo> <description> (James05feb97) + Con_restore_game(total_commands, &input[1][0]); + return(1); //quit the console + break; + //--------------------------------- + case 28: // BLTFXON (Paul12feb97) + SetBltFx(); + Print_to_console("blit fx enabled"); + return(0); + break; + //--------------------------------- + case 29: // BLTFXOFF (Paul12feb97) + ClearBltFx(); + Print_to_console("blit fx disabled"); + return(0); + break; + //--------------------------------- + case 30: // TIMEON (Paul12feb97) + Con_start_timer(total_commands, &input[1][0]); + Print_to_console("timer display on"); + return(0); + break; + //--------------------------------- + case 31: // TIMEOFF (Paul12feb97) + displayTime = 0; + Print_to_console("timer display off"); + return(0); + break; + //--------------------------------- + case 32: // TEXT (James25feb97) + displayTextNumbers = 1-displayTextNumbers; + if (displayTextNumbers) + Print_to_console("text numbers on"); + else + Print_to_console("text numbers off"); + return(0); + break; + //--------------------------------- + case 33: // SHOWVAR <varNo> (James19mar97) + ShowVar(&input[1][0]); // add variable to watch-list + return(0); + break; + //--------------------------------- + case 34: // HIDEVAR <varNo> (James19mar97) + HideVar(&input[1][0]); // remove variable from watch-list + return(0); + break; + //--------------------------------- + case 35: // VERSION (James21mar97) + Con_display_version(); + return(0); + break; + //--------------------------------- + case 36: // SOFT (James07apr97) + if (RenderSoft()) + Print_to_console("Software Rendering already enabled"); + else + { + Print_to_console("Software Rendering enabled"); + CloseBackgroundLayer(); // unlock from memory (because used in hardware rendering) + } + return(0); + break; + //--------------------------------- + case 37: // HARD (James07apr97) + rv = RenderHard(); + + if (rv==RDERR_NOHARDWARE) + Print_to_console("Hardware Rendering not available"); + else if (rv==RDERR_ALREADYON) + Print_to_console("Hardware Rendering already enabled"); + else + Print_to_console("Hardware Rendering enabled"); + return(0); + break; + //--------------------------------- + case 38: // ANIMTEST + Con_start((uint8*)"32"); // automatically do "s 32" to run the text/speech testing start-script + + Print_to_console("Setting flag 'system_testing_anims'"); + Var_set((uint8*)"912", &input[1][0]); // same as typing "VAR 912 <value>" at the console + + return(1); + break; + //--------------------------------- + case 39: // TEXTTEST + Con_start((uint8*)"33"); // automatically do "s 33" to run the text/speech testing start-script + + Print_to_console("Setting flag 'system_testing_text'"); + Var_set((uint8*)"1230", &input[1][0]); // same as typing "VAR 1230 <value>" at the console + + displayTextNumbers=1; + Print_to_console("text numbers on"); + + return(1); + break; + //--------------------------------- + case 40: // LINETEST + Con_start((uint8*)"33"); // automatically do "s 33" to run the text/speech testing start-script + + Print_to_console("Setting var 1230 (system_testing_text):"); + Var_set((uint8*)"1230", &input[1][0]); // same as typing "VAR 1230 <value>" at the console + + Print_to_console("Setting var 1264 (system_test_line_no):"); + Var_set((uint8*)"1264", &input[2][0]); // same as typing "VAR 1264 <value>" at the console + + displayTextNumbers=1; + Print_to_console("text numbers on"); + + return(1); + break; + //--------------------------------- + case 41: // GRAB (James27jun97) + grabbingSequences = 1-grabbingSequences; + if (grabbingSequences) + Print_to_console("PCX-grabbing enabled"); + else + Print_to_console("PCX-grabbing disabled"); + return(0); + break; + //--------------------------------- + case 42: // EVENTS (James11july97) + Con_display_events(); + return(0); + break; + //--------------------------------- + case 43: // SFX (James 16july97) + wantSfxDebug = 1-wantSfxDebug; + if (wantSfxDebug) + Print_to_console("SFX logging activated (see zebug.txt)"); + else + Print_to_console("SFX logging deactivated"); + return(0); + break; + //--------------------------------- + case 44: // ENGLISH (James31july97) + InitialiseFontResourceFlags(DEFAULT_TEXT); + Print_to_console("Default fonts selected"); + return(0); + break; + //--------------------------------- + case 45: // FINNISH (James31july97) + InitialiseFontResourceFlags(FINNISH_TEXT); + Print_to_console("Finnish fonts selected"); + return(0); + break; + //--------------------------------- + case 46: // POLISH (James31july97) + InitialiseFontResourceFlags(POLISH_TEXT); + Print_to_console("Polish fonts selected"); + return(0); + break; + //--------------------------------- + default: //ignores 'yes' and 'no' (for example) + Print_to_console("??"); + return(0); + break; + //--------------------------------- + } + break; + + + case 1: //checks for YES and NO and returns the 1 or 2 to the calling code + switch(j) + { + case 4: //YES + return(1); + + case 5: //NO + return(2); + + default: //ignore yes and no for example + Print_to_console("??"); + return(0); + + } + break; + + } + break; + } + } + Print_to_console("?"); //couldn't find a proper match + return(0); + +} +//----------------------------------------------------------------------------------------------------------------------- +void Con_help(void) //Tony13Aug96 +{ +// print out a list of commands + + int command; + int scrolls=0; + char c; + + + + + + Scroll_console(); + + + + for (command=0; command < TOTAL_CONSOLE_COMMANDS; command++) + { + Print_to_console((char *)commands[command]); + Build_display(); + scrolls++; + + + if (scrolls==18) + { + Temp_print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(!KeyWaiting()); + + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + + Clear_console_line(); //clear the Press Esc message ready for the new line + scrolls=0; + } + + + } + + +} +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +void Con_print(uint8 *ascii, uint32 pen, uint32 paper) //Em(13Apr95tw) +{ +//print pixels in closen pen number - no clipping on this one +//where file is the graphic file of ascii characters + + _frameHeader *head; + uint8 *charSet, *charPtr; + int chr, x=0; + + + + charSet = res_man.Res_open(CONSOLE_FONT_ID); // open font file + + do + { + +// Zdebug("#%d", *(ascii) ); + + chr = (int) *(ascii); + chr-=32; + + head = (_frameHeader *)FetchFrameHeader(charSet,chr); + charPtr = (uint8 *)(head+1); + + Con_colour_block( x, head->width, head->height, pen, paper, charPtr); + + x+=head->width+1; //move on the x coordinate + ascii++; + } + while(*(ascii)); + + + res_man.Res_close(CONSOLE_FONT_ID); // close font file + +} +//----------------------------------------------------------------------------------------------------------------------- +void Con_colour_block(int x, int width, int height, uint32 pen, uint32 paper, uint8 *sprite_data_ad) //Em(26Apr96tw) +{ + int deltaX,xx,yy; + char *ad; + + + deltaX = con_width-width; + + ad = (char *) console_sprite->ad; + ad += (con_width*(con_depth-con_chr_height))+x; //locate bottom character row + + for (yy=0;yy<height;yy++) + { + for (xx=0;xx<width;xx++) + { if (pen = *(sprite_data_ad++)) //color + *(ad++)= (uint8)CON_PEN; + else *(ad++)= (uint8) paper; + } + ad+=deltaX; + } +} +//----------------------------------------------------------------------------------------------------------------------- + + + + + + +//----------------------------------------------------------------------------------------------------------------------- +void Con_fatal_error(char *format,...) //Tony17Oct96 +{ +//use this to alert the user of a major problem from which we cannot allow the game to continue +//while in console mode the user will still be ble to use the console commands - which may be useful +//this message is also written to the Zdebug file in case the console itself blows up + + va_list arg_ptr; //variable argument pointer + char buf[150]; + uint8 white[4] = {255,255,255,0}; // (James 05mar97) + + + SetPalette(CON_PEN, 1, white, RDPAL_INSTANT); // set text colour in case screen is faded down! (James 05mar97) + + va_start(arg_ptr,format); + _vsnprintf( buf, 150, format, arg_ptr); + + this_screen.background_layer_id=0; //in case error in display loop + + Zdebug("CON_FATAL_ERROR:"); + Zdebug(buf); //write to file first in-case the screen is screwed up and we never see the console + + Print_to_console(buf); + Print_to_console("fatal error, sword2 must terminate :-( (%d)", ID); + Tconsole(0); //mode 0 so all commands are available but quit will terminate the game + + + Close_game(); //should down game services - free's mallocs, etc. + + RestoreDisplay(); //reset the Windows stuff + CloseAppWindow(); // + + exit(0); +} +//----------------------------------------------------------------------------------------------------------------------- +void Var_check(uint8 *pointer) //Tony8Jan97 +{ + int var; + + + sscanf((char*)pointer,"%d",&var); + + Print_to_console("%d", *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4*var) ); + +} +//----------------------------------------------------------------------------------------------------------------------- +void Var_set(uint8 *pointer, uint8 *p2) //Tony8Jan97 +{ + int var; + int val; + + + + sscanf((char*)pointer,"%d",&var); + sscanf((char*)p2,"%d",&val); + + + Print_to_console("was %d", *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4*var) ); + + *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* var)=val; + + Print_to_console("now %d", val); + +} + +//----------------------------------------------------------------------------------------------------------------------- +void ShowVar(uint8 *varNoPtr) // James19mar97 +{ + int32 showVarNo=0; + int32 varNo; + + + sscanf((char*)varNoPtr,"%d",&varNo); // 'varNo' is what we want to add + + // search for a spare slot in the watch-list, but also watch out for this variable already being in the list + while ((showVarNo < MAX_SHOWVARS) && (showVar[showVarNo] != 0) && (showVar[showVarNo] != varNo)) + showVarNo++; + + if (showVarNo < MAX_SHOWVARS) // if we've found a spare slot or the variable's already there + { + if (showVar[showVarNo]==0) // empty slot + { + showVar[showVarNo] = varNo; // add it to the list at this slot + Print_to_console("var(%d) added to the watch-list", varNo); + } + else + Print_to_console("var(%d) already in the watch-list!", varNo); + } + else + Print_to_console("Sorry - no more allowed - hide one or extend the system watch-list"); + +} +//----------------------------------------------------------------------------------------------------------------------- +void HideVar(uint8 *varNoPtr) // James19mar97 +{ + int32 showVarNo=0; + int32 varNo; + + + sscanf((char*)varNoPtr,"%d",&varNo); // 'varNo' is what we want to remove + + while ((showVarNo < MAX_SHOWVARS) && (showVar[showVarNo] != varNo)) // search for 'varNo' in the watch-list + showVarNo++; + + if (showVarNo < MAX_SHOWVARS) // if we've found 'varNo' in the list + { + showVar[showVarNo] = 0; // clear this slot + Print_to_console("var(%d) removed from watch-list", varNo); + } + else + Print_to_console("Sorry - can't find var(%d) in the list", varNo); + +} +//----------------------------------------------------------------------------------------------------------------------- +void Con_list_savegames(void) // (James05feb97) Tony1Apr97 +{ + uint8 description[SAVE_DESCRIPTION_LEN]; + int j, scrolls=0; + char c; + + for (j=0;j<100;j++) + if (GetSaveDescription(j, description) == SR_OK) //if there is a save game print the name + { Print_to_console("%d: \"%s\"", j, description); + + scrolls++; + Build_display(); + + if (scrolls==18) + { + Temp_print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(!KeyWaiting()); + + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + + Clear_console_line(); //clear the Press Esc message ready for the new line + scrolls=0; + } + } + +} +//----------------------------------------------------------------------------------------------------------------------- +#define SR_OK 0x00000000 // ok No worries +#define SR_ERR_FILEOPEN 0x00000001 // can't open file Could create file for saving, or couldn't find file for loading +#define SR_ERR_INCOMPATIBLE 0x00000002 // (RestoreGame only) incompatible savegame data Savegame file is obsolete. (Won't happen after development stops) +#define SR_ERR_READFAIL 0x00000003 // (RestoreGame only) failed on reading savegame file Something screwed up during the fread() +#define SR_ERR_WRITEFAIL 0x00000004 // (SaveGame only) failed on writing savegame file Something screwed up during the fwrite() - could be hard-drive full..? +//----------------------------------------------------------------------------------------------------------------------- +void Con_save_game(int total_commands, uint8 *slotString, uint8 *description) // James05feb97 +{ + uint16 slotNo; + uint32 rv; + + if ((mouse_status) || (mouse_mode_locked)) // if mouse if off, or system menu is locked off + { + Print_to_console("WARNING: Cannot save game while control menu unavailable!"); + return; + } + + if (total_commands >= 3) // SAVE <slot> <description> + { + if (Is_number(slotString)) + { + slotNo = atoi((char*)slotString); + + rv = SaveGame(slotNo,description); + + if (rv == SR_OK) + Print_to_console("Saved game \"%s\" to file \"savegame.%.3d\"", description, slotNo); + + else if (rv == SR_ERR_FILEOPEN) + Print_to_console("ERROR: Cannot open file \"savegame.%.3d\"", slotNo); + + else // SR_ERR_WRITEFAIL + Print_to_console("ERROR: Write error on file \"savegame.%.3d\"", slotNo); + } + } + else + Print_to_console("Syntax Error: type SAVE (slot_number) (description)"); +} +//----------------------------------------------------------------------------------------------------------------------- +void Con_restore_game(int total_commands, uint8 *slotString) // James05feb97 +{ + uint16 slotNo; + uint8 description[SAVE_DESCRIPTION_LEN]; + uint32 rv; + + + if ((mouse_status) || (mouse_mode_locked)) // if mouse if off, or system menu is locked off + { + Print_to_console("WARNING: Cannot restore game while control menu unavailable!"); + return; + } + + if (total_commands >= 2) // RESTORE <slot> + { + if (Is_number(slotString)) + { + slotNo = atoi((char*)slotString); + rv = RestoreGame(slotNo); + + if (rv == SR_OK) + { + GetSaveDescription(slotNo, description); + Print_to_console("Restored game \"%s\" from file \"savegame.%.3d\"", description, slotNo); + } + else if (rv == SR_ERR_FILEOPEN) + Print_to_console("ERROR: Cannot open file \"savegame.%.3d\"", slotNo); + + else if (rv == SR_ERR_INCOMPATIBLE) + Print_to_console("ERROR: \"savegame.%.3d\" is no longer compatible with current player/variable resources", slotNo); + + else // SR_ERR_READFAIL + Print_to_console("ERROR: Read error on file \"savegame.%.3d\"", slotNo); + } + } + else + Print_to_console("Syntax Error: type RESTORE (slot_number)"); +} +//----------------------------------------------------------------------------------------------------------------------- +void Con_start_timer(int total_commands, uint8 *slotString) // Paul12feb97 +{ + + if (total_commands >= 2) // RESTORE <slot> + { + if (Is_number(slotString)) + { + startTime = timeGetTime() - (atoi((char*)slotString) * 1000); + } + } + else + { + if (startTime = 0) + startTime = timeGetTime(); + } + displayTime = 1; + +} +//----------------------------------------------------------------------------------------------------------------------- +uint8 Is_number(uint8 *ascii) // James05feb97 +{ + while (*ascii) // until we reach the null terminator + { + if ((*ascii >= '0') && (*ascii <= '9')) + ascii++; + else + return(0); + } + + return(1); +} +//----------------------------------------------------------------------------------------------------------------------- +void Con_display_version(void) // James27mar97 +{ + struct tm *time; + time_t t; + char dateStamp[255]; + char version[6]; + + strcpy(version,(char*)version_string+HEAD_LEN); + *(((unsigned char *)&t)) = *(version_string+14); + *(((unsigned char *)&t)+1) = *(version_string+15); + *(((unsigned char *)&t)+2) = *(version_string+16); + *(((unsigned char *)&t)+3) = *(version_string+17); + + time = localtime( &t ); + sprintf(dateStamp,"%s", asctime( time ) ); + dateStamp[24]=0; // fudge over the newline character! + + Print_to_console("\"Broken Sword II\" (c) Revolution Software 1997."); + Print_to_console("v%s created on %s for %s", version, dateStamp, unencoded_name+HEAD_LEN); + Scroll_console(); + + // THE FOLLOWING LINES ARE TO BE COMMENTED OUT OF THE FINAL VERSION +// Print_to_console("This program has a personalised fingerprint encrypted into the code."); +// Print_to_console("If this CD was not sent directly to you by Virgin Interactive or Revolution Software"); +// Print_to_console("then please contact James Long at Revolution on (+44) 1904 639698."); +// Scroll_console(); +} +//----------------------------------------------------------------------------------------------------------------------- +// typedef struct +// { +// uint32 id; +// uint32 interact_id; +// } _event_unit; +//----------------------------------------------------------------------------------------------------------------------- +void Con_display_events() // (James11july97) +{ + uint32 j; + uint32 target; + uint32 script; + + + Print_to_console("EVENT LIST:"); + + for (j=0; j<MAX_events; j++) + { + if (event_list[j].id) + { + target = event_list[j].id; + script = event_list[j].interact_id; + + Print_to_console("slot %d: id = %s (%d)", j, FetchObjectName(target), target); + Print_to_console(" script = %s (%d) pos %d", FetchObjectName(script/65536), script/65536, script%65536); + } + } +} +//------------------------------------------------------------------------------------ + +#else // not debug + +void Print_to_console(char *format,...) {}; +void Temp_print_to_console(char *format,...) {}; +void Clear_console_line(void) {}; +void Scroll_console(void) {}; +void Init_console(void) {}; +void StartConsole(void) {}; + +#endif // _DEBUG diff --git a/sword2/console.h b/sword2/console.h new file mode 100644 index 0000000000..1dd5014044 --- /dev/null +++ b/sword2/console.h @@ -0,0 +1,81 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef C_ONSOLE_H +#define C_ONSOLE_H + +#include "driver/driver96.h" +#include "memory.h" + +#ifdef _DEBUG + +void Init_console(void); //Tony9Sept96 +uint32 One_console(void); //Tony12Aug96 +void StartConsole(void); //Tony12Aug96 +void EndConsole(void); //Tony9Oct96 + +void Con_fatal_error(char *format,...); +void Print_to_console(char *format,...); //Tony13Aug96 +void Temp_print_to_console(char *format,...); //Tony13Aug96 +void Scroll_console(void); //Tony13Aug96 +void Clear_console_line(void); //Tony13Aug96 + +extern mem *console_sprite; +extern uint32 con_y; +extern uint32 con_depth; +extern uint32 con_width; + +extern uint8 grabbingSequences; +extern uint8 wantSfxDebug; // sfx debug file enabled/disabled from console + + +#else // _DEBUG +/* +#define Init_console NULL +#define One_console NULL +#define StartConsole NULL +#define EndConsole NULL +*/ + +void Init_console(void); +uint32 One_console(void); +void StartConsole(void); +void EndConsole(void); + +// 'Con_fatal_error' commands map to ExitWithReport +// so we show errors in a window rather than our development game console +#define Con_fatal_error ExitWithReport + +//#define Print_to_console NULL +//#define Temp_print_to_console NULL +//#define Clear_console_line NULL +//#define Scroll_console NULL +void Print_to_console(char *format,...); +void Temp_print_to_console(char *format,...); +void Clear_console_line(void); +void Scroll_console(void); +//#define Var_check NULL +//#define Var_set NULL + +#endif // _DEBUG + +extern uint32 console_status; + + +#endif diff --git a/sword2/controls.cpp b/sword2/controls.cpp new file mode 100644 index 0000000000..d30a2ebf99 --- /dev/null +++ b/sword2/controls.cpp @@ -0,0 +1,2917 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//----------------------------------------------------------------------------------------------------------------------- +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +//#include "src\driver96.h" + +#include "build_display.h" +#include "console.h" +#include "controls.h" +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "interpreter.h" +#include "layers.h" +#include "logic.h" +#include "maketext.h" // for font resource variables +#include "mem_view.h" +#include "memory.h" +#include "mouse.h" +#include "protocol.h" +#include "resman.h" +#include "router.h" +#include "save_rest.h" +#include "sound.h" // for FN_stop_music() +#include "startup.h" +#include "sword2.h" + +//----------------------------------------------------------------------------------------------------------------------- +#define WINDOW_RES 2016 +#define MAX_STRING_LEN 64 // 20 was too low; better to be safe ;) + +#define CHARACTER_OVERLAP 2 // overlap characters by 3 pixels +//----------------------------------------------------------------------------------------------------------------------- + +void Create_surface_image(_spriteInfo *sprite, uint32 *surface, uint32 res, uint32 x, uint32 y, uint32 pc); +void Build_surfaces(void); +void Build_chr_surfaces(void); +void Kill_chr_surfaces(void); +void Kill_surfaces(void); +void Renew_surfaces(void); +void Engine_string(uint32 x, uint32 y, uint32 res, uint32 *surface_list, uint8 *buf); +void Kill_mini_surfaces(void); +void Build_mini_surfaces(void); +uint32 Generic_mini_control(uint32 text_id); +uint32 Pixel_text_length(uint8 *buf, uint32 res); +void Control_error(char* text); + +//----------------------------------------------------------------------------------------------------------------------- + +#define WINDOW_X 0 + +#define REST_X 84 +#define REST_Y 40 + +#define SLAB_X (REST_X+30) +#define SLAB_Y (REST_Y+32) + +#define REST_BUT_X 130 +#define REST_BUT_Y 377 + +#define CAN_BUT_X 350 +#define CAN_BUT_Y 377 + +#define UP_BUT_X 516 +#define UP_BUT_Y 85 + +#define DOWN_BUT_X 516 +#define DOWN_BUT_Y 329 + +#define ZUP_BUT_Y 85-20 + +#define ZDOWN_BUT_Y 329+21 + +#define SLAB_Y_SPACING 35 + +#define QUIT_X 203 +#define QUIT_Y 104 + +#define OPTION_X 45 +#define OPTION_Y 40 +#define OPTION_W 552 + +#define OPT_BUT_W 53 +#define OPT_BUT_H 32 + +#define OPT_OK_X (OPTION_X+(OPTION_W/3)-(OPT_BUT_W/2)) +#define OPT_OK_Y (OPTION_Y+368-(OPT_BUT_W/2)) + +#define OPT_CAN_X (OPTION_X+350) +#define OPT_CAN_Y (OPT_OK_Y) + +#define SLIDER_TRK_X (OPTION_X+264) +#define SLIDER_TRK_W 132 +#define SLIDER_TRK_H 27 +#define SLIDER_W 38 + +#define OBJ_LABEL_X (SLIDER_TRK_X-5) +#define OBJ_LABEL_Y (OPTION_Y+60) + +#define SUBTITLE_X (OPTION_X+465) +#define SUBTITLE_Y (OBJ_LABEL_Y) + +#define STEREO_X (SLIDER_TRK_X-5) +#define STEREO_Y (OPTION_Y+253) + +#define MUSIC_TRK_Y (OPTION_Y+121) +#define SPEECH_TRK_Y (OPTION_Y+168) +#define FX_TRK_Y (OPTION_Y+214) +#define GRFX_TRK_Y (OPTION_Y+301) + +#define MUTE_W 40 +#define MUTE_H 32 +#define MUTE_X (SUBTITLE_X+OPT_BUT_W/2-MUTE_W/2) + +#define GRFX_ICON_X (OPTION_X+450) +#define GRFX_ICON_Y (OPTION_Y+270) + +//-------------------------------------------- + +uint32 panel_surface; +_spriteInfo panel_sprite; + +_spriteInfo slab_sprite[8]; +uint32 slab_surface[8]; + +_spriteInfo chr_sprite; + +#define SIZE_OF_CHAR_SET (256-32) // our fonts start on SPACE character (32) +uint32 chr_surface[SIZE_OF_CHAR_SET]; +uint32 red_chr_surface[SIZE_OF_CHAR_SET]; + +_spriteInfo can_button_sprite[2]; +uint32 can_button_surface[2]; +uint32 can_button_state=0; +uint32 touching_can_button=0; + +_spriteInfo button_sprite[2]; +uint32 button_surface[2]; +uint32 restore_button_state=0; +uint32 touching_restore_button=0; + +_spriteInfo up_button_sprite[2]; +uint32 up_button_surface[2]; +uint32 up_button_state=0; +uint32 touching_up_button=0; + +_spriteInfo down_button_sprite[2]; +uint32 down_button_surface[2]; +uint32 down_button_state=0; +uint32 touching_down_button=0; + +_spriteInfo zup_button_sprite[2]; +uint32 zup_button_surface[2]; +uint32 zup_button_state=0; +uint32 touching_zup_button=0; + +_spriteInfo zdown_button_sprite[2]; +uint32 zdown_button_surface[2]; +uint32 zdown_button_state=0; +uint32 touching_zdown_button=0; + +_spriteInfo grfx_icon_sprite[4]; +uint32 grfx_icon_surface[4]; + +uint8 *charSet; +uint8 *red_charSet; + +_frameHeader *head; + +uint32 base_slot=0; + +uint8 subtitles; // text selected +uint8 speechSelected; +uint8 stereoReversed = 0; + +uint8 current_graphics_level; + +//----------------------------------------------------------------------------------------------------------------------- +uint32 Restore_control(void) //Tony20Mar97 +{ +//well, this is nice and hard wired - not the way to do it really + +// returns 0 for no restore +// 1 for restored ok + + + uint8 *colTablePtr=NULL; + + int breakOut=0; + uint32 j; + uint8 black[4]={0,0,0,0}; + char key_press=0; + uint8 description[SAVE_DESCRIPTION_LEN]; + uint8 buf[8][SAVE_DESCRIPTION_LEN]; + + uint8 chr; + int char_no; + _mouseEvent *me; + + uint8 restore_text[MAX_STRING_LEN]; + uint8 cancel_text[MAX_STRING_LEN]; + uint8 *text; + + + uint32 slab_text_x; + uint32 slab_text_y; + + uint32 slot=1000; //nothing selected + uint32 clicked_slot; + uint32 cur_slot_states[9]; + + int scroll_rate=0; + //static uint32 base_slot=0; + + int first=0; + + uint32 rv; // return value for RestoreGame + uint32 res; //result from primer game cycle + + + int names_built=0; //0 redo, else dont + + + +//do some driver stuff +// for (j=0;j<1000;j++) +// ResetRenderEngine(); + + + + +//buttons unpressed + restore_button_state=0; + can_button_state=0; + + + + + Build_surfaces(); + Build_chr_surfaces(); + +//fetch the 'restore' text + text = FetchTextLine( res_man.Res_open(149618690/SIZE), 149618690&0xffff ); // open text file & get the line + strcpy((char*)&restore_text[0], (char*)text+2); +//fetch the 'cancel' text + text = FetchTextLine( res_man.Res_open(149618689/SIZE), 149618689&0xffff ); // open text file & get the line + strcpy((char*)&cancel_text[0], (char*)text+2); +//blimey, life's never easy is it? + + +//control loop + while (1) + { + //-------------------------------------------------- + // Service windows + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q during the smacker + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + { names_built=0; + slot=1000; + if (ServiceWindows() == RDERR_APPCLOSED) + break; + } + //-------------------------------------------------- + + EraseBackBuffer(); + + +//print panel + if (DrawSurface(&panel_sprite, panel_surface)==RDERR_SURFACELOST) + Renew_surfaces(); + + +//print words on panel + Engine_string(REST_BUT_X+32, REST_BUT_Y, controls_font_id, chr_surface, restore_text); + Engine_string(CAN_BUT_X+32, REST_BUT_Y, controls_font_id, chr_surface, cancel_text); + + + + + slab_text_y=76; + +// print slabs + for (j=0;j<8;j++) + { + + if (slot==base_slot+j) //red + { slab_sprite[((base_slot+j)&3)+4].y=SLAB_Y+(j*SLAB_Y_SPACING); + DrawSurface(&slab_sprite[((base_slot+j)&3)+4], slab_surface[((base_slot+j)&3)+4] ); + } + else + { slab_sprite[((base_slot+j)&3)].y=SLAB_Y+(j*SLAB_Y_SPACING); + DrawSurface(&slab_sprite[((base_slot+j)&3)], slab_surface[((base_slot+j)&3)] ); + } + +// print save name on slab if a game is saved in this slot + if (!names_built) + { if (GetSaveDescription(base_slot+j, description) == SR_OK) //if there is a savegame at this slot + { + cur_slot_states[j]=1; //slot used + + if (!description[0]) + Con_fatal_error("NULL file name passed from GetSaveDescription!"); + + // print the name on the slab + sprintf((char*)buf[j], "%d. %s", base_slot+j, description ); + } + else + { + sprintf((char*)buf[j], "%d.", base_slot+j); //simply print the number + cur_slot_states[j]=0; //slot not used + } + } + + + char_no=0; + + slab_text_x=SLAB_X+16; + + + do + { + chr = buf[j][char_no]; + chr-=32; //got true chr$ + + chr_sprite.x=slab_text_x; + + + chr_sprite.scale=0; + chr_sprite.type= RDSPR_NOCOMPRESSION+RDSPR_TRANS; + chr_sprite.blend= 0; + + if (slot==base_slot+j) + { + head = (_frameHeader *)FetchFrameHeader(red_charSet, chr); + + chr_sprite.w=head->width; + chr_sprite.h=head->height; + + chr_sprite.y=slab_text_y+2; + DrawSurface(&chr_sprite, red_chr_surface[chr]); //print + } + else + { + head = (_frameHeader *)FetchFrameHeader(charSet, chr); + + chr_sprite.w=head->width; + chr_sprite.h=head->height; + + chr_sprite.y=slab_text_y; + DrawSurface(&chr_sprite, chr_surface[chr]); //print + } + + slab_text_x+=head->width-CHARACTER_OVERLAP; + char_no++; + } + while(buf[j][char_no]); + + + slab_text_y+=SLAB_Y_SPACING; + } + + + + names_built=1; //dont GetSaveDescription each cycle + +//print buttons +//print restore button + DrawSurface(&button_sprite[restore_button_state], button_surface[restore_button_state] ); + +//print cancel button + DrawSurface(&can_button_sprite[can_button_state], can_button_surface[can_button_state] ); + +//print up button + DrawSurface(&up_button_sprite[up_button_state], up_button_surface[up_button_state] ); + +//print down button + DrawSurface(&down_button_sprite[down_button_state], down_button_surface[down_button_state] ); + +//print zup button + DrawSurface(&zup_button_sprite[zup_button_state], zup_button_surface[zup_button_state] ); + +//print zdown button + DrawSurface(&zdown_button_sprite[zdown_button_state], zdown_button_surface[zdown_button_state] ); + + + ProcessMenu(); + + if (!first) + { + first++; + SetFullPalette(CONTROL_PANEL_PALETTE); // see Build_display.cpp (James17jun97) + } + + FlipScreens(); + + + + + + + + +//mouse over buttons? +//restore + if ((mousex>REST_BUT_X)&&(mousex<REST_BUT_X+24)&&((mousey+40)>REST_BUT_Y)&&((mousey+40)<REST_BUT_Y+24)) + touching_restore_button=1; //mouse over button + else //not over so release even if pressed previously + { restore_button_state=0; + touching_restore_button=0; + } + +//cancel + if ((mousex>CAN_BUT_X)&&(mousex<CAN_BUT_X+24)&&((mousey+40)>CAN_BUT_Y)&&((mousey+40)<CAN_BUT_Y+24)) + touching_can_button=1; //mouse over button + else //not over so release even if pressed previously + { can_button_state=0; + touching_can_button=0; + } + +//up + if ((mousex>UP_BUT_X)&&(mousex<UP_BUT_X+17)&&((mousey+40)>UP_BUT_Y)&&((mousey+40)<UP_BUT_Y+17)) + touching_up_button=1; //mouse over button + else //not over so release even if pressed previously + { up_button_state=0; + touching_up_button=0; + } + +//down + if ((mousex>DOWN_BUT_X)&&(mousex<DOWN_BUT_X+17)&&((mousey+40)>DOWN_BUT_Y)&&((mousey+40)<DOWN_BUT_Y+17)) + touching_down_button=1; //mouse over button + else //not over so release even if pressed previously + { down_button_state=0; + touching_down_button=0; + } + +//up + if ((mousex>UP_BUT_X)&&(mousex<UP_BUT_X+17)&&((mousey+40)>ZUP_BUT_Y)&&((mousey+40)<ZUP_BUT_Y+17)) + touching_zup_button=1; //mouse over button + else //not over so release even if pressed previously + { zup_button_state=0; + touching_zup_button=0; + } + +//down + if ((mousex>DOWN_BUT_X)&&(mousex<DOWN_BUT_X+17)&&((mousey+40)>ZDOWN_BUT_Y)&&((mousey+40)<ZDOWN_BUT_Y+17)) + touching_zdown_button=1; //mouse over button + else //not over so release even if pressed previously + { zdown_button_state=0; + touching_zdown_button=0; + } + + + + + + + +//check mouse clicked on a slab + me = MouseEvent(); //get mouse event + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed + { + if ((mousex>SLAB_X)&&(mousex<SLAB_X+384)&&((mousey+40)>SLAB_Y)&&((mousey+40)<SLAB_Y+SLAB_Y_SPACING*8)) + { + clicked_slot=((mousey+40)-SLAB_Y)/SLAB_Y_SPACING; + + //Zdebug("clicked slab %d", slot); + + if (cur_slot_states[clicked_slot]) //a selectable slot + { + if (slot!= clicked_slot+base_slot) //can select if not selected now + slot=clicked_slot+base_slot; //now selected + + else slot=1000; //else deselect + } + } + +// clicking on the restore button + if (touching_restore_button) + restore_button_state=1; //button now down + +// clicking on the cancel button + if (touching_can_button) + can_button_state=1; //button now down + +// clicking on the up button + if (touching_up_button) + { up_button_state=1; //button now down + scroll_rate=0; + } + +// clicking on the down button + if (touching_down_button) + { down_button_state=1; //button now down + scroll_rate=0; + } +// clicking on the zup button + if (touching_zup_button) + { zup_button_state=1; //button now down + scroll_rate=0; + } + +// clicking on the zdown button + if (touching_zdown_button) + { zdown_button_state=1; //button now down + scroll_rate=0; + } + + + } + +//check for releasing the mouse button over a button + if ((key_press==13)||((me!=NULL)&&(me->buttons&RD_LEFTBUTTONUP))) //there's a mouse event to be processed + { + if ((key_press==13)||((touching_restore_button)&&(restore_button_state))) + { + restore_button_state=0; + + if (slot!=1000) //restore the game! + { + breakOut=1; + + rv = RestoreGame(slot); + + if (rv == SR_OK) + { +// DEAD=0; //in case we were dead - well we're not anymore! + +// prime system with a game cycle + Reset_render_lists(); // reset the graphic 'buildit' list before a new logic list (see FN_register_frame) + Reset_mouse_list(); // reset the mouse hot-spot list (see FN_register_mouse & FN_register_frame) + + res = LLogic.Process_session(); + + if (res) + Con_fatal_error("restart 1st cycle failed??"); + + + // Control_error("restored OK :)"); + + Kill_surfaces(); + Kill_chr_surfaces(); + + return(1); + } + else + { +// Save & Restore error codes + +// ERROR CODE VALUE MEANING REASON +// ========== ===== ======= ====== +// SR_OK 0x00000000 // ok No worries +// SR_ERR_FILEOPEN 0x00000001 // can't open file Could create file for saving, or couldn't find file for loading +// SR_ERR_INCOMPATIBLE 0x00000002 // (RestoreGame only) incompatible savegame data Savegame file is obsolete. (Won't happen after development stops) +// SR_ERR_READFAIL 0x00000003 // (RestoreGame only) failed on reading savegame file Something screwed up during the fread() +// SR_ERR_WRITEFAIL 0x00000004 // (SaveGame only) failed on writing savegame file Something screwed up during the fwrite() - could be hard-drive full..? + + + // WE NEED A MESSAGE BOX TO INDICATE FAILED SAVE - DON'T HALT THE GAME! + + if (rv == SR_ERR_FILEOPEN) + Control_error((char*)(FetchTextLine( res_man.Res_open(213516670/SIZE), 213516670&0xffff)+2)); +// Restore failed - could not open file + + else if (rv == SR_ERR_INCOMPATIBLE) + Control_error((char*)(FetchTextLine( res_man.Res_open(213516671/SIZE), 213516671&0xffff)+2)); +// Restore failed - incompatible savegame data + + else // SR_ERR_READFAIL + Control_error((char*)(FetchTextLine( res_man.Res_open(213516673/SIZE), 213516673&0xffff)+2)); +// Restore failed + } + } + } + else if ((touching_can_button)&&(can_button_state)) //quit the screen + breakOut=1; + + else if (touching_up_button) + up_button_state=0; + + else if (touching_down_button) + down_button_state=0; + + else if (touching_zup_button) + zup_button_state=0; + + else if (touching_zdown_button) + zdown_button_state=0; + + } + + + +//scrolling downward + if ( ((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(down_button_state)&&(base_slot<92)) + { base_slot++; + names_built=0; + } + +//scrolling upward + if (((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(up_button_state)&&(base_slot)) + { base_slot--; + names_built=0; + } + +//scrolling zdownward + if (((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(zdown_button_state)&&(base_slot<92)) + { base_slot+=8; + if (base_slot>92) + base_slot=92; + + names_built=0; + } + +//scrolling zupward + if (((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(zup_button_state)&&(base_slot)) + { base_slot-=8; + if (base_slot>92) + base_slot=0; + + names_built=0; + } + + +//update scroll stuff + scroll_rate++; + + + + +//----- + key_press=0; + if (KeyWaiting()) + { + ReadKey(&key_press); //kill the key we just pressed + if (key_press==27) //ESC + break; + } + + + if (breakOut) + { + break; //quit this stuff - ap will eventually close in the mainloop + } + + + } //while + + + Kill_surfaces(); + Kill_chr_surfaces(); + + return(0); +} +//----------------------------------------------------------------------------------------------------------------------- +void Create_surface_image(_spriteInfo *sprite, uint32 *surface, uint32 res, uint32 x, uint32 y, uint32 pc) //TonyMarch97 +{ + uint8 *file, *colTablePtr=NULL; + _animHeader *anim_head; + _frameHeader *frame_head; + _cdtEntry *cdt_entry; + //_spriteInfo spriteInfo; + uint32 spriteType=RDSPR_TRANS; + + + file = res_man.Res_open(res); // open anim resource file & point to base + + + anim_head = FetchAnimHeader( file ); + cdt_entry = FetchCdtEntry( file, pc ); + frame_head = FetchFrameHeader( file, pc ); + + + + + if (anim_head->blend) + spriteType += RDSPR_BLEND; + + if ((cdt_entry->frameType) & FRAME_FLIPPED) //if the frame is to be flipped (only really applicable to frames using offsets) + spriteType += RDSPR_FLIP; + + switch (anim_head->runTimeComp) // what compression was used? + { + case NONE: + spriteType += RDSPR_NOCOMPRESSION; + break; + case RLE256: + spriteType += RDSPR_RLE256; + break; + case RLE16: + spriteType += RDSPR_RLE16; + colTablePtr = (uint8*)(anim_head+1) + anim_head->noAnimFrames*sizeof(_cdtEntry); + // points to just after last cdt_entry, ie. start of colour table + break; + } + + sprite->x = x; + sprite->y = y; + sprite->w = frame_head->width; + sprite->h = frame_head->height; + sprite->scale = 0; +// spriteInfo.scaledWidth = build_unit->scaled_width; +// spriteInfo.scaledHeight = build_unit->scaled_height; + sprite->type = spriteType; + sprite->blend = anim_head->blend; + sprite->data = (uint8*)(frame_head+1); // points to just after frame header, ie. start of sprite data +// spriteInfo.colourTable = colTablePtr; + + +// Zdebug("w %d h %d", frame_head->width, frame_head->height); + + CreateSurface(sprite, surface); + + + res_man.Res_close(res); //release the anim resource + +} +//----------------------------------------------------------------------------------------------------------------------- +void Build_surfaces(void) //Tony27March97 +{ + + +//setup the control window + Create_surface_image(&panel_sprite, &panel_surface, WINDOW_RES, WINDOW_X, REST_Y, 0); + + + +//setup slabs as surfaces + Create_surface_image(&slab_sprite[0], &slab_surface[0], 2006, SLAB_X, 0, 0); + Create_surface_image(&slab_sprite[1], &slab_surface[1], 2007, SLAB_X, 0, 0); + Create_surface_image(&slab_sprite[2], &slab_surface[2], 2008, SLAB_X, 0, 0); + Create_surface_image(&slab_sprite[3], &slab_surface[3], 2009, SLAB_X, 0, 0); + +//now the red selected panels + Create_surface_image(&slab_sprite[4], &slab_surface[4], 2006, SLAB_X, 0, 1); + Create_surface_image(&slab_sprite[5], &slab_surface[5], 2007, SLAB_X, 0, 1); + Create_surface_image(&slab_sprite[6], &slab_surface[6], 2008, SLAB_X, 0, 1); + Create_surface_image(&slab_sprite[7], &slab_surface[7], 2009, SLAB_X, 0, 1); + +//restore button + Create_surface_image(&button_sprite[0], &button_surface[0], 2002, REST_BUT_X, REST_BUT_Y, 0); + Create_surface_image(&button_sprite[1], &button_surface[1], 2002, REST_BUT_X, REST_BUT_Y, 1); + +//cancel button + Create_surface_image(&can_button_sprite[0], &can_button_surface[0], 2002, CAN_BUT_X, CAN_BUT_Y, 0); + Create_surface_image(&can_button_sprite[1], &can_button_surface[1], 2002, CAN_BUT_X, CAN_BUT_Y, 1); + +//up button + Create_surface_image(&up_button_sprite[0], &up_button_surface[0], 2067, UP_BUT_X, UP_BUT_Y, 0); + Create_surface_image(&up_button_sprite[1], &up_button_surface[1], 2067, UP_BUT_X, UP_BUT_Y, 1); + +//down button + Create_surface_image(&down_button_sprite[0], &down_button_surface[0], 1986, DOWN_BUT_X, DOWN_BUT_Y, 0); + Create_surface_image(&down_button_sprite[1], &down_button_surface[1], 1986, DOWN_BUT_X, DOWN_BUT_Y, 1); + +//zup button + Create_surface_image(&zup_button_sprite[0], &zup_button_surface[0], 1982, UP_BUT_X, ZUP_BUT_Y, 0); + Create_surface_image(&zup_button_sprite[1], &zup_button_surface[1], 1982, UP_BUT_X, ZUP_BUT_Y, 1); + +//zdown button + Create_surface_image(&zdown_button_sprite[0], &zdown_button_surface[0], 1988, DOWN_BUT_X, ZDOWN_BUT_Y, 0); + Create_surface_image(&zdown_button_sprite[1], &zdown_button_surface[1], 1988, DOWN_BUT_X, ZDOWN_BUT_Y, 1); + +} +//----------------------------------------------------------------------------------------------------------------------- +void Build_chr_surfaces(void) //tony2Apr97 +{ + + int j; + + +//sort out the font + charSet = res_man.Res_open(controls_font_id); //open font file + red_charSet = res_man.Res_open(red_font_id); //open font file + + +//set up the chr$ set frame surfaces + chr_sprite.scale=0; + chr_sprite.type= RDSPR_NOCOMPRESSION+RDSPR_TRANS; + chr_sprite.blend= 0; + + for (j=0; j < SIZE_OF_CHAR_SET; j++) + { //normal + head = (_frameHeader *)FetchFrameHeader(charSet,j); + chr_sprite.data = (uint8 *)(head+1); + chr_sprite.w=head->width; + chr_sprite.h=head->height; + CreateSurface(&chr_sprite, &chr_surface[j]); + +// red + head = (_frameHeader *)FetchFrameHeader(red_charSet,j); + chr_sprite.data = (uint8 *)(head+1); + chr_sprite.w=head->width; + chr_sprite.h=head->height; + CreateSurface(&chr_sprite, &red_chr_surface[j]); + + } + + res_man.Res_close(controls_font_id); //close font file + res_man.Res_close(red_font_id); //close font file + +} +//----------------------------------------------------------------------------------------------------------------------- +void Kill_surfaces(void) //Tony27March97 +{ + +//remove the surfaces + DeleteSurface(panel_surface); + DeleteSurface(slab_surface[0]); + DeleteSurface(slab_surface[1]); + DeleteSurface(slab_surface[2]); + DeleteSurface(slab_surface[3]); + DeleteSurface(slab_surface[4]); + DeleteSurface(slab_surface[5]); + DeleteSurface(slab_surface[6]); + DeleteSurface(slab_surface[7]); + + DeleteSurface(button_surface[0]); + DeleteSurface(button_surface[1]); + + DeleteSurface(can_button_surface[0]); + DeleteSurface(can_button_surface[1]); + + DeleteSurface(up_button_surface[0]); + DeleteSurface(up_button_surface[1]); + + DeleteSurface(down_button_surface[0]); + DeleteSurface(down_button_surface[1]); + + DeleteSurface(zup_button_surface[0]); + DeleteSurface(zup_button_surface[1]); + + DeleteSurface(zdown_button_surface[0]); + DeleteSurface(zdown_button_surface[1]); +} +//----------------------------------------------------------------------------------------------------------------------- +void Kill_chr_surfaces(void) //Tony2Apr97 +{ + int j; + + + +//release chr$ set surfaces + for (j=0; j < SIZE_OF_CHAR_SET; j++) + { //normal + DeleteSurface(chr_surface[j]); +// red + DeleteSurface(red_chr_surface[j]); + } +} +//----------------------------------------------------------------------------------------------------------------------- +void Renew_surfaces(void) //Tony27March97 +{ + Kill_surfaces(); + Kill_chr_surfaces(); + Build_surfaces(); + Build_chr_surfaces(); +} +//----------------------------------------------------------------------------------------------------------------------- + + + + + + +//----------------------------------------------------------------------------------------------------------------------- +void Save_control(void) //Tony1Apr97 not a joke +{ +//largely the same as the restore code + + + uint8 *colTablePtr=NULL; + + int breakOut=0; + uint32 j; + uint8 black[4]={0,0,0,0}; + char key_press; + uint8 description[SAVE_DESCRIPTION_LEN]; + uint8 buf[8][SAVE_DESCRIPTION_LEN]; + uint8 ed_buf[SAVE_DESCRIPTION_LEN]; + int char_no; + uint8 chr; + _mouseEvent *me; + int esc_release=0; + + uint32 slab_text_x; + uint32 slab_text_y; + + uint32 slot=1000; //nothing selected + uint32 clicked_slot, edit_screen_slot=1000; + uint32 cur_slot_states[9]; + + int scroll_rate=0; +// static uint32 base_slot=0; + + int edit_pos, first_chr, flash=0; + + uint8 save_text[MAX_STRING_LEN]; + uint8 cancel_text[MAX_STRING_LEN]; + uint8 *text; + + int first=0; + + uint32 rv; // return value for SaveGame + + uint32 edit_width; + + int names_built=0; + + + +//buttons unpressed + restore_button_state=0; + can_button_state=0; + + +//do some driver stuff +// ResetRenderEngine(); + + + +//sort out the font + charSet = res_man.Res_open(controls_font_id); //open font file + red_charSet = res_man.Res_open(red_font_id); //open font file + + chr_sprite.scale=0; + chr_sprite.type= RDSPR_NOCOMPRESSION+RDSPR_TRANS; + chr_sprite.blend= 0; + + + + + Build_surfaces(); + Build_chr_surfaces(); + +//fetch the 'save' text + text = FetchTextLine( res_man.Res_open(149618691/SIZE), 149618691&0xffff ); // open text file & get the line + strcpy((char*)&save_text[0], (char*)text+2); +//fetch the 'cancel' text + text = FetchTextLine( res_man.Res_open(149618689/SIZE), 149618689&0xffff ); // open text file & get the line + strcpy((char*)&cancel_text[0], (char*)text+2); +//blimey, life's never easy is it? + + + +//control loop + while (1) + { + //-------------------------------------------------- + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q during the smacker + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + // Service windows + while (!gotTheFocus) + { names_built=0; + edit_screen_slot=1000; + if (ServiceWindows() == RDERR_APPCLOSED) + break; + } + //-------------------------------------------------- + + EraseBackBuffer(); + + +//print panel + if (DrawSurface(&panel_sprite, panel_surface)==RDERR_SURFACELOST) + Renew_surfaces(); + +//print words on panel + Engine_string(REST_BUT_X+32, REST_BUT_Y, controls_font_id, chr_surface, save_text); + Engine_string(CAN_BUT_X+32, REST_BUT_Y, controls_font_id, chr_surface, cancel_text); + + + + + + slab_text_y=76; + +// print slabs + for (j=0;j<8;j++) + { + if (edit_screen_slot!=j) + { slab_sprite[((base_slot+j)&3)].y=SLAB_Y+(j*SLAB_Y_SPACING); + DrawSurface(&slab_sprite[((base_slot+j)&3)], slab_surface[((base_slot+j)&3)] ); + +// print save name on slab if a game is saved in this slot + if (!names_built) + { + if (GetSaveDescription(base_slot+j, description) == SR_OK) //if there is a savegame at this slot + { cur_slot_states[j]=1; //slot used + + if (!description[0]) + Con_fatal_error("NULL file name passed from GetSaveDescription!"); + + + // print the name on the slab + sprintf((char*)buf[j], "%d. %s", base_slot+j, description ); + } + else + { sprintf((char*)buf[j], "%d.", base_slot+j); //simply print the number + cur_slot_states[j]=0; //slot not used + } + } + + char_no=0; + + slab_text_x=SLAB_X+16; + + + do + { + chr = buf[j][char_no]; + chr-=32; //got true chr$ + + chr_sprite.x=slab_text_x; + + head = (_frameHeader *)FetchFrameHeader(charSet, chr); + + chr_sprite.w=head->width; + chr_sprite.h=head->height; + + chr_sprite.y=slab_text_y; + DrawSurface(&chr_sprite, chr_surface[chr]); //print + + slab_text_x+=head->width-CHARACTER_OVERLAP; // overlap characters by 3 pixels; + char_no++; + } + while(buf[j][char_no]); + } + slab_text_y+=SLAB_Y_SPACING; + } + + names_built=1; + +//draw the typing slab and text if we are still editing + if (edit_screen_slot!=1000) //we are typing a name in + { + + flash++; + if (flash<7) + ed_buf[edit_pos]='_'; + else + ed_buf[edit_pos]=' '; //by putting a space in we'll always have a chr$ in the buffer + + if (flash==14) + flash=0; + + +// now draw the current edit line +// draw a red slab + slab_sprite[(clicked_slot&3)+4].y=SLAB_Y+(edit_screen_slot*SLAB_Y_SPACING); + DrawSurface(&slab_sprite[(clicked_slot&3)+4], slab_surface[(clicked_slot&3)+4] ); + +// draw the text line + char_no=0; + edit_width=0; //total pixel width of text being typed in + slab_text_x=SLAB_X+16; + +// print the chr$ + do + { + chr = ed_buf[char_no]; + chr-=32; //got true chr$ + + chr_sprite.x=slab_text_x; + + head = (_frameHeader *)FetchFrameHeader(red_charSet, chr); + + chr_sprite.w=head->width; + chr_sprite.h=head->height; + + chr_sprite.y=SLAB_Y+(edit_screen_slot*SLAB_Y_SPACING)+5; //why 5? when its 2 on restore???????? + DrawSurface(&chr_sprite, red_chr_surface[chr]); //print + + slab_text_x+=head->width-CHARACTER_OVERLAP; + edit_width+=head->width-CHARACTER_OVERLAP; + + char_no++; + } + while(ed_buf[char_no]); + } + + + + +//print buttons +//print restore button + DrawSurface(&button_sprite[restore_button_state], button_surface[restore_button_state] ); + +//print cancel button + DrawSurface(&can_button_sprite[can_button_state], can_button_surface[can_button_state] ); + +//print up button + DrawSurface(&up_button_sprite[up_button_state], up_button_surface[up_button_state] ); + +//print down button + DrawSurface(&down_button_sprite[down_button_state], down_button_surface[down_button_state] ); + +//print zup button + DrawSurface(&zup_button_sprite[zup_button_state], zup_button_surface[zup_button_state] ); + +//print zdown button + DrawSurface(&zdown_button_sprite[zdown_button_state], zdown_button_surface[zdown_button_state] ); + + + ProcessMenu(); + + + + +//mouse over buttons? +//restore + if ((mousex>REST_BUT_X)&&(mousex<REST_BUT_X+24)&&((mousey+40)>REST_BUT_Y)&&((mousey+40)<REST_BUT_Y+24)) + touching_restore_button=1; //mouse over button + else //not over so release even if pressed previously + { restore_button_state=0; + touching_restore_button=0; + } + +//cancel + if ((mousex>CAN_BUT_X)&&(mousex<CAN_BUT_X+24)&&((mousey+40)>CAN_BUT_Y)&&((mousey+40)<CAN_BUT_Y+24)) + touching_can_button=1; //mouse over button + else //not over so release even if pressed previously + { can_button_state=0; + touching_can_button=0; + } + +//up + if ((mousex>UP_BUT_X)&&(mousex<UP_BUT_X+17)&&((mousey+40)>UP_BUT_Y)&&((mousey+40)<UP_BUT_Y+17)) + touching_up_button=1; //mouse over button + else //not over so release even if pressed previously + { up_button_state=0; + touching_up_button=0; + } + +//down + if ((mousex>DOWN_BUT_X)&&(mousex<DOWN_BUT_X+17)&&((mousey+40)>DOWN_BUT_Y)&&((mousey+40)<DOWN_BUT_Y+17)) + touching_down_button=1; //mouse over button + else //not over so release even if pressed previously + { down_button_state=0; + touching_down_button=0; + } + +//up + if ((mousex>UP_BUT_X)&&(mousex<UP_BUT_X+17)&&((mousey+40)>ZUP_BUT_Y)&&((mousey+40)<ZUP_BUT_Y+17)) + touching_zup_button=1; //mouse over button + else //not over so release even if pressed previously + { zup_button_state=0; + touching_zup_button=0; + } + +//down + if ((mousex>DOWN_BUT_X)&&(mousex<DOWN_BUT_X+17)&&((mousey+40)>ZDOWN_BUT_Y)&&((mousey+40)<ZDOWN_BUT_Y+17)) + touching_zdown_button=1; //mouse over button + else //not over so release even if pressed previously + { zdown_button_state=0; + touching_zdown_button=0; + } + + + + + + + +//check mouse clicked on a slab + me = MouseEvent(); //get mouse event + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed + { + if ((mousex>SLAB_X)&&(mousex<SLAB_X+384)&&((mousey+40)>SLAB_Y)&&((mousey+40)<SLAB_Y+SLAB_Y_SPACING*8)) + { + edit_screen_slot=((mousey+40)-SLAB_Y)/SLAB_Y_SPACING; + clicked_slot=edit_screen_slot+base_slot; + + Zdebug("+ %d %d", edit_screen_slot, clicked_slot); + +// now edit the line +// take a copy of the string + + for (j=0;j<SAVE_DESCRIPTION_LEN;j++) + ed_buf[j]=0; //zero the string + + + sprintf((char*)ed_buf, "%d. ", clicked_slot); //simply print the number - assume no name in panel + first_chr=strlen((char*)ed_buf); + edit_pos=first_chr; + + + if (GetSaveDescription(clicked_slot, description) == SR_OK) //if there is a savegame at this slot + { + sprintf((char*)ed_buf, "%d. %s", clicked_slot, description ); + edit_pos=strlen((char*)ed_buf); //recalculate cursor pos to end of string + } + + Zdebug("first %d [%s]", first_chr, ed_buf); + + } + +// clicking on the restore button + if (touching_restore_button) + restore_button_state=1; //button now down + +// clicking on the cancel button + if (touching_can_button) + can_button_state=1; //button now down + +// clicking on the up button + if (touching_up_button) + { up_button_state=1; //button now down + scroll_rate=0; + } + +// clicking on the down button + if (touching_down_button) + { down_button_state=1; //button now down + scroll_rate=0; + } +// clicking on the zup button + if (touching_zup_button) + { zup_button_state=1; //button now down + scroll_rate=0; + } + +// clicking on the zdown button + if (touching_zdown_button) + { zdown_button_state=1; //button now down + scroll_rate=0; + } + + + } + +//check for releasing the mouse button over a button + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONUP)) //there's a mouse event to be processed + { + + if ((touching_restore_button)&&(restore_button_state)) + { + restore_button_state=0; + + if ((edit_screen_slot!=1000)&&(edit_pos!=first_chr)) //we are editing and have a legal file name typed in + { //then save game - can also be saved when you press RETurn + ed_buf[edit_pos]=0; //remove cursor/[space] + + rv = SaveGame(clicked_slot, (uint8*)&ed_buf[first_chr]); + + if (rv == SR_OK) + { breakOut=1; //finished + if ((edit_screen_slot>6)&(base_slot<92)) + base_slot++; + } + else + { +// Save & Restore error codes + +// ERROR CODE VALUE MEANING REASON +// ========== ===== ======= ====== +// SR_OK 0x00000000 // ok No worries +// SR_ERR_FILEOPEN 0x00000001 // can't open file Could create file for saving, or couldn't find file for loading +// SR_ERR_INCOMPATIBLE 0x00000002 // (RestoreGame only) incompatible savegame data Savegame file is obsolete. (Won't happen after development stops) +// SR_ERR_READFAIL 0x00000003 // (RestoreGame only) failed on reading savegame file Something screwed up during the fread() +// SR_ERR_WRITEFAIL 0x00000004 // (SaveGame only) failed on writing savegame file Something screwed up during the fwrite() - could be hard-drive full..? + + // WE NEED A MESSAGE BOX TO INDICATE FAILED SAVE - DON'T HALT THE GAME! + + if (rv == SR_ERR_FILEOPEN) + Control_error((char*)(FetchTextLine( res_man.Res_open(213516674/SIZE), 213516674&0xffff)+2)); +// Save failed - could not open file + + else // SR_ERR_WRITEFAIL + Control_error((char*)(FetchTextLine( res_man.Res_open(213516676/SIZE), 213516676&0xffff)+2)); +// Save failed + } + } + } + else if ((touching_can_button)&&(can_button_state)) //quit the screen + breakOut=1; + + else if (touching_up_button) + up_button_state=0; + + else if (touching_down_button) + down_button_state=0; + + else if (touching_zup_button) + zup_button_state=0; + + else if (touching_zdown_button) + zdown_button_state=0; + + } + + + +//scrolling downward + if ( ((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(down_button_state)&&(base_slot<92)) + { base_slot++; + names_built=0; + + if (edit_screen_slot!=1000) + { edit_screen_slot--; + if (base_slot>clicked_slot) + edit_screen_slot=1000; + } + } + +//scrolling upward + if (((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(up_button_state)&&(base_slot)) + { base_slot--; + names_built=0; + + if (edit_screen_slot!=1000) + { edit_screen_slot++; + if ((base_slot+7)<clicked_slot) + edit_screen_slot=1000; + } + } + +//scrolling zdownward + if (((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(zdown_button_state)&&(base_slot<92)) + { base_slot+=8; + names_built=0; + + if (base_slot>92) + base_slot=92; + + edit_screen_slot=1000; //no more editing + } + +//scrolling zupward + if (((scroll_rate<1)||(scroll_rate>12))&&(!(scroll_rate&3))&&(zup_button_state)&&(base_slot)) + { base_slot-=8; + names_built=0; + + if (base_slot>92) + base_slot=0; + edit_screen_slot=1000; //no more editing + } + + + + +//update scroll stuff + scroll_rate++; + + + + +//----- + +//deal with user input if the user is inputting + if ((edit_screen_slot!=1000)&&(KeyWaiting())) //we are typing a name in + { + ReadKey(&key_press); + + if (!key_press) //escape sequences + { + } + else if (key_press==27) //ESC + { + edit_screen_slot=1000; //quit this edit + esc_release=42; //stop the ESC key auto-repeating after this and quiting the save control + } + else if (key_press==13) //RETurn + { + if (edit_pos!=first_chr) + { //save game + ed_buf[edit_pos]=0; //remove cursor/[space] + + Zdebug("%d %d %s", first_chr, edit_pos, &ed_buf[first_chr]); + + rv = SaveGame(clicked_slot, (uint8*)&ed_buf[first_chr]); + + if (rv == SR_OK) + { breakOut=1; //finished + if ((edit_screen_slot>6)&(base_slot<92)) + base_slot++; + } + else + { +// Save & Restore error codes + +// ERROR CODE VALUE MEANING REASON +// ========== ===== ======= ====== +// SR_OK 0x00000000 // ok No worries +// SR_ERR_FILEOPEN 0x00000001 // can't open file Could create file for saving, or couldn't find file for loading +// SR_ERR_INCOMPATIBLE 0x00000002 // (RestoreGame only) incompatible savegame data Savegame file is obsolete. (Won't happen after development stops) +// SR_ERR_READFAIL 0x00000003 // (RestoreGame only) failed on reading savegame file Something screwed up during the fread() +// SR_ERR_WRITEFAIL 0x00000004 // (SaveGame only) failed on writing savegame file Something screwed up during the fwrite() - could be hard-drive full..? + + + // WE NEED A MESSAGE BOX TO INDICATE FAILED SAVE - DON'T HALT THE GAME! + + if (rv == SR_ERR_FILEOPEN) + Control_error((char*)(FetchTextLine( res_man.Res_open(213516674/SIZE), 213516674&0xffff)+2)); +// Save failed - could not open file + + else // SR_ERR_WRITEFAIL + Control_error((char*)(FetchTextLine( res_man.Res_open(213516676/SIZE), 213516676&0xffff)+2)); +// Save failed + } + } + else edit_screen_slot=1000; //dont save an empty slot and cancel editing + } + + else if (key_press==8) //delete + { + if (edit_pos!=first_chr) + { + ed_buf[edit_pos]=0; //delete cursor chr$ + edit_pos--; + ed_buf[edit_pos]=0; + } + } + else if ((key_press<32)||(key_press>'z')) + Zdebug("save ignoring key - %d", key_press); + else + { +// if (edit_pos<(20)) //less one to leave room for the cursor + + + if ((edit_width<350)&&(edit_pos<SAVE_DESCRIPTION_LEN-2)) + ed_buf[edit_pos++]=key_press; + else //end of line has been reached, so keep replacing last letter + ed_buf[edit_pos-1]=key_press; //replace + } + } + else if (KeyWaiting()) + { + ReadKey(&key_press); //kill the key we just pressed + if ((key_press==27)&&(!esc_release)) //ESC + breakOut=1; + + else if (key_press!=27) + esc_release=0; + } + else if (!KeyWaiting()) + esc_release=0; + + + + + + + + + + + + if (breakOut) + { + break; //quit this stuff - ap will eventually close in the mainloop + } + + if (!first) + { + first++; + SetFullPalette(CONTROL_PANEL_PALETTE); // see Build_display.cpp (James17jun97) + } + + FlipScreens(); + + } //while + + + Kill_surfaces(); + Kill_chr_surfaces(); + +} +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +void Quit_control(void) //Tony2Apr97 +{ + uint32 res; + + + res=Generic_mini_control(149618692); //quit text + + + if (!res) + return; //just return to game + +//avoid corruption when windows kicks back in + EraseBackBuffer(); + FlipScreens(); + EraseBackBuffer(); + FlipScreens(); + + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + + exit(0); + +} +//----------------------------------------------------------------------------------------------------------------------- +void Restart_control(void) //Tony4Apr97 +{ + uint32 res; + uint32 temp_demo_flag; + + + res=Generic_mini_control(149618693); //restart text + + + if (!res) + return; //just return to game + + + Kill_music(); // Stop music instantly! (James22aug97) + + + DEAD=0; //in case we were dead - well we're not anymore! + +//clean up surface memory + Kill_mini_surfaces(); + EraseBackBuffer(); + //ProcessMenu(); //draw menu + FlipScreens(); + + +//restart the game +//clear all memory and reset the globals + + temp_demo_flag = DEMO; + + res_man.Remove_all_res(); //remove all resources from memory, including player object & global variables + SetGlobalInterpreterVariables((int32*)(res_man.Res_open(1)+sizeof(_standardHeader))); //reopen global variables resource & send address to interpreter - it won't be moving + res_man.Res_close(1); + + DEMO = temp_demo_flag; + + FreeAllRouteMem(); // free all the route memory blocks from previous game + + Start_game(); // call the same function that first started us up + + +//prime system with a game cycle + Reset_render_lists(); // reset the graphic 'buildit' list before a new logic list (see FN_register_frame) + Reset_mouse_list(); // reset the mouse hot-spot list (see FN_register_mouse & FN_register_frame) + + CloseMenuImmediately(); + + + //--------------------------------------------------------------- + // FOR THE DEMO - FORCE THE SCROLLING TO BE RESET! (James29may97) + // - this is taken from FN_init_background + this_screen.scroll_flag = 2; // switch on scrolling (2 means first time on screen) + //--------------------------------------------------------------- + + res = LLogic.Process_session(); + + if (res) + Con_fatal_error("restart 1st cycle failed??"); + + this_screen.new_palette=99; // (JEL08oct97) so palette not restored immediately after control panel - we want to fade up instead! + +} +//----------------------------------------------------------------------------------------------------------------------- +uint32 Generic_mini_control(uint32 text_id) //Tony2Apr97 +{ + +// returns 1 for OK pressed +// returns 0 for CANCEL pressed + + uint8 black[4]={0,0,0,0}; + int breakOut=0; + char c; + + uint8 quit_text[MAX_STRING_LEN]; + uint8 ok_text[MAX_STRING_LEN]; + uint8 cancel_text[MAX_STRING_LEN]; + uint8 *text; + + _mouseEvent *me; + + int first=0; + + int text_len; + + #define OK_BUT_X (QUIT_X+40) + #define OK_BUT_Y (QUIT_Y+110) + #define CAN_BU_Y (QUIT_Y+172) + + +//do some driver stuff +// ResetRenderEngine(); + + +//fetch the 'quit' text + text = FetchTextLine( res_man.Res_open(text_id/SIZE), text_id&0xffff ); //quit or restart + strcpy((char*)&quit_text[0], (char*)text+2); + text_len = Pixel_text_length(&quit_text[0], controls_font_id); + + +//fetch the 'ok' text + text = FetchTextLine( res_man.Res_open(149618688/SIZE), 149618688&0xffff ); //ok + strcpy((char*)&ok_text[0], (char*)text+2); + +//fetch the 'cancel' text + text = FetchTextLine( res_man.Res_open(149618689/SIZE), 149618689&0xffff ); //cancel + strcpy((char*)&cancel_text[0], (char*)text+2); +//blimey, life's never easy is it? + + + + +//buttons unpressed + restore_button_state=0; + can_button_state=0; + + +//build surfaces + Build_mini_surfaces(); + + + +//control loop + while (1) + { + //-------------------------------------------------- + // Service windows + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + //-------------------------------------------------- + + + EraseBackBuffer(); + + +//print panel + if (DrawSurface(&panel_sprite, panel_surface)==RDERR_SURFACELOST) + { + Kill_mini_surfaces(); + Build_mini_surfaces(); + } + +//print words on panel quit_x+81 + Engine_string(310-(text_len/2), QUIT_Y+30, controls_font_id, chr_surface, quit_text); // quit + Engine_string(QUIT_X+67, QUIT_Y+110, controls_font_id, chr_surface, ok_text); // ok + Engine_string(QUIT_X+67, QUIT_Y+172, controls_font_id, chr_surface, cancel_text); // cancel + +//print buttons +//print ok button + DrawSurface(&button_sprite[restore_button_state], button_surface[restore_button_state] ); +//print cancel button + DrawSurface(&can_button_sprite[can_button_state], can_button_surface[can_button_state] ); + +//keep menu up too + ProcessMenu(); + +//user can ESC quit + if (KeyWaiting()) + { + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + } + + +//mouse over ok button? + if ((mousex>OK_BUT_X)&&(mousex<OK_BUT_X+24)&&((mousey+40)>OK_BUT_Y)&&((mousey+40)<OK_BUT_Y+24)) + touching_restore_button=1; //mouse over button + else //not over so release even if pressed previously + { restore_button_state=0; + touching_restore_button=0; + } +//mouse over cancel button? + if ((mousex>OK_BUT_X)&&(mousex<OK_BUT_X+24)&&((mousey+40)>CAN_BU_Y)&&((mousey+40)<CAN_BU_Y+24)) + touching_can_button=1; //mouse over button + else //not over so release even if pressed previously + { can_button_state=0; + touching_can_button=0; + } + + +//pressing on a button + me = MouseEvent(); //get mouse event + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed + { + if (touching_restore_button) + restore_button_state=1; + + if (touching_can_button) + can_button_state=1; + + } + else if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONUP)) + { + if ((touching_restore_button)&&(restore_button_state)) //quit the game + { + return(1); + } + + if ((touching_can_button)&&(can_button_state)) + { can_button_state=0; + breakOut=1; + } + + } + + + if (breakOut) + break; + + FlipScreens(); + if (!first) + { + first++; + SetFullPalette(CONTROL_PANEL_PALETTE); // see Build_display.cpp (James17jun97) + } + } + + + Kill_mini_surfaces(); + + return(0); +} +//----------------------------------------------------------------------------------------------------------------------- +void Build_mini_surfaces(void) //tony3Apr97 +{ + Create_surface_image(&panel_sprite, &panel_surface, 1996, QUIT_X, QUIT_Y, 0); +//ok button + Create_surface_image(&button_sprite[0], &button_surface[0], 2002, OK_BUT_X, OK_BUT_Y, 0); + Create_surface_image(&button_sprite[1], &button_surface[1], 2002, OK_BUT_X, OK_BUT_Y, 1); +//cancel button + Create_surface_image(&can_button_sprite[0], &can_button_surface[0], 2002, OK_BUT_X, CAN_BU_Y, 0); + Create_surface_image(&can_button_sprite[1], &can_button_surface[1], 2002, OK_BUT_X, CAN_BU_Y, 1); + + Build_chr_surfaces(); +} +//----------------------------------------------------------------------------------------------------------------------- +void Kill_mini_surfaces(void) //tony3Apr97 +{ + DeleteSurface(panel_surface); +//ok button + DeleteSurface(button_surface[0]); + DeleteSurface(button_surface[1]); +//cancel button + DeleteSurface(can_button_surface[0]); + DeleteSurface(can_button_surface[1]); + + Kill_chr_surfaces(); +} +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +void Engine_string(uint32 x, uint32 y, uint32 res, uint32 *surface_list, uint8 *buf) //tony2Apr97 +{ +//takes fonts as sprites and prints transparently to screen +//requires the chr$ surfaces have been setup + + int char_no=0; + int chr; + uint8 *chars; + + + chars = res_man.Res_open(res); //open font file + + chr_sprite.scale=0; + chr_sprite.type= RDSPR_NOCOMPRESSION+RDSPR_TRANS; + chr_sprite.blend= 0; + + + chr_sprite.x=x; + chr_sprite.y=y; + + do + { + chr = buf[char_no]; + chr-=32; //got true chr$ + + head = (_frameHeader *)FetchFrameHeader(chars, chr); + + chr_sprite.w=head->width; + chr_sprite.h=head->height; + + DrawSurface(&chr_sprite, surface_list[chr]); //print + + chr_sprite.x+=head->width-CHARACTER_OVERLAP; + char_no++; + } + while(buf[char_no]); + + res_man.Res_close(res); //close font file +} +//----------------------------------------------------------------------------------------------------------------------- +uint32 Pixel_text_length(uint8 *buf, uint32 res) //tony4Apr97 +{ + + int char_no=0; + int chr; + uint8 *chars; + uint32 width=0; + + + chars = res_man.Res_open(res); //open font file + + do + { + chr = buf[char_no]; + chr-=32; //got true chr$ + + head = (_frameHeader *)FetchFrameHeader(chars, chr); + + width+=head->width-CHARACTER_OVERLAP; + + char_no++; + } + while(buf[char_no]); + + res_man.Res_close(res); //close font file + + return(width); +} +//----------------------------------------------------------------------------------------------------------------------- +void Control_error(char* text) //Tony13May97 +{ +//print a message on screen, wait for key, return + + _mouseEvent *me; + char c; + + DisplayMsg( (uint8*)text, 0 ); // 2nd param is duration + + + while (1) + { + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q during the smacker + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + //-------------------------------------------------- + + if (KeyWaiting()) + { + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + } + + me = MouseEvent(); //get mouse event + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed + break; + + + + } + + + RemoveMsg(); // Removes the message. + +} + +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +int32 ReadOptionSettings(void) //pete10Jun97 +{ + // settings file is 9 bytes long, bytes 1 to 3 = music, speech and fx volumes + // bytes 4 to 6 = music, speech and fx mute states, byte 7 = grfx level + // byte 8 = subtitle state and byte 9 = object label state. + + uint8 buff[10]; + FILE *fp; + + if ((fp = fopen("Settings.dat","rb"))== NULL) + return (1); + + if (fread(buff,1,10,fp) != 10) + { + fclose(fp); + return (2); + } + + fclose(fp); + + SetMusicVolume(buff[0]); + SetSpeechVolume(buff[1]); + SetFxVolume(buff[2]); + MuteMusic(buff[3]); + MuteSpeech(buff[4]); + MuteFx(buff[5]); + + + UpdateGraphicsLevel(GetRenderType(), buff[6]); // (James13jun97) + + speechSelected = !buff[4]; + subtitles = buff[7]; + pointerTextSelected = buff[8]; + + if (buff[9] != stereoReversed) + ReverseStereo(); + + stereoReversed = buff[9]; + + return (0); +} +//----------------------------------------------------------------------------------------------------------------------- +int32 WriteOptionSettings(void) //pete10Jun97 +{ + uint8 buff[10]; + FILE *fp; + + buff[0] = GetMusicVolume(); + buff[1] = GetSpeechVolume(); + buff[2] = GetFxVolume(); + buff[3] = IsMusicMute(); + buff[4] = IsSpeechMute(); + buff[5] = IsFxMute(); + buff[6] = GetRenderType(); + buff[7] = subtitles; + buff[8] = pointerTextSelected; + buff[9] = stereoReversed; + + if ((fp = fopen("Settings.dat","wb"))== NULL) + return (1); + + if (fwrite(buff,1,10,fp) != 10) + { + fclose(fp); + return (2); + } + + fclose(fp); + + return (0); +} + + +//----------------------------------------------------------------------------------------------------------------------- +void Build_option_surfaces(void) //pete6Jun97 +{ + Create_surface_image(&panel_sprite, &panel_surface, 3405, 0, OPTION_Y, 0); +// object label button + Create_surface_image(&up_button_sprite[0], &up_button_surface[0], 3687, OBJ_LABEL_X,OBJ_LABEL_Y, 0); + Create_surface_image(&up_button_sprite[1], &up_button_surface[1], 3687, OBJ_LABEL_X,OBJ_LABEL_Y, 1); +//subtitle button + Create_surface_image(&down_button_sprite[0], &down_button_surface[0], 3687, SUBTITLE_X, SUBTITLE_Y, 0); + Create_surface_image(&down_button_sprite[1], &down_button_surface[1], 3687, SUBTITLE_X, SUBTITLE_Y, 1); +//ok button + Create_surface_image(&button_sprite[0], &button_surface[0], 901, OPT_OK_X, OPT_OK_Y, 0); + Create_surface_image(&button_sprite[1], &button_surface[1], 901, OPT_OK_X, OPT_OK_Y, 1); +//cancel button + Create_surface_image(&can_button_sprite[0], &can_button_surface[0], 901, OPT_CAN_X, OPT_CAN_Y, 0); + Create_surface_image(&can_button_sprite[1], &can_button_surface[1], 901, OPT_CAN_X, OPT_CAN_Y, 1); +//sliders + Create_surface_image(&slab_sprite[0], &slab_surface[0], 3406, SLIDER_TRK_X,MUSIC_TRK_Y,0); // music slider + Create_surface_image(&slab_sprite[1], &slab_surface[1], 3406, SLIDER_TRK_X,SPEECH_TRK_Y,0); // speech slider + Create_surface_image(&slab_sprite[2], &slab_surface[2], 3406, SLIDER_TRK_X,FX_TRK_Y,0); // fx slider + Create_surface_image(&slab_sprite[3], &slab_surface[3], 3406, SLIDER_TRK_X,GRFX_TRK_Y,0); // graphics slider +//mute buttons + Create_surface_image(&zup_button_sprite[0], &zup_button_surface[0], 3315, MUTE_X, MUSIC_TRK_Y-4, 0); // music mute + Create_surface_image(&zup_button_sprite[1], &zup_button_surface[1], 3315, MUTE_X, MUSIC_TRK_Y-4, 1); + Create_surface_image(&zdown_button_sprite[0], &zdown_button_surface[0],3315, MUTE_X, SPEECH_TRK_Y-3, 0);// speech mute + Create_surface_image(&zdown_button_sprite[1], &zdown_button_surface[1],3315, MUTE_X, SPEECH_TRK_Y-3, 1); + Create_surface_image(&slab_sprite[4], &slab_surface[4], 3315, MUTE_X,FX_TRK_Y-4,0); // fx mute + Create_surface_image(&slab_sprite[5], &slab_surface[5], 3315, MUTE_X,FX_TRK_Y-4,1); +//graphics level icon + Create_surface_image(&grfx_icon_sprite[0], &grfx_icon_surface[0], 256, GRFX_ICON_X, GRFX_ICON_Y, 0); // lowest grapihics level icon + Create_surface_image(&grfx_icon_sprite[1], &grfx_icon_surface[1], 256, GRFX_ICON_X, GRFX_ICON_Y, 1); // medium low grapihics level icon + Create_surface_image(&grfx_icon_sprite[2], &grfx_icon_surface[2], 256, GRFX_ICON_X, GRFX_ICON_Y, 2); // mewdium high grapihics level icon + Create_surface_image(&grfx_icon_sprite[3], &grfx_icon_surface[3], 256, GRFX_ICON_X, GRFX_ICON_Y, 3); // highest grapihics level icon +//reverse stereo button + Create_surface_image(&slab_sprite[6], &slab_surface[6], 3687, STEREO_X,STEREO_Y,0); + Create_surface_image(&slab_sprite[7], &slab_surface[7], 3687, STEREO_X,STEREO_Y,1); + + Build_chr_surfaces(); +} +//----------------------------------------------------------------------------------------------------------------------- +void Kill_option_surfaces(void) //pete6Jun97 +{ + DeleteSurface(panel_surface); +//object label button + DeleteSurface(up_button_surface[0]); + DeleteSurface(up_button_surface[1]); +//subtitle button + DeleteSurface(down_button_surface[0]); + DeleteSurface(down_button_surface[1]); +//ok button + DeleteSurface(button_surface[0]); + DeleteSurface(button_surface[1]); +//cancel button + DeleteSurface(can_button_surface[0]); + DeleteSurface(can_button_surface[1]); +//sliders + DeleteSurface(slab_surface[0]); + DeleteSurface(slab_surface[1]); + DeleteSurface(slab_surface[2]); + DeleteSurface(slab_surface[3]); +//mute buttons + DeleteSurface(zup_button_surface[0]); + DeleteSurface(zup_button_surface[1]); + DeleteSurface(zdown_button_surface[0]); + DeleteSurface(zdown_button_surface[1]); + DeleteSurface(slab_surface[4]); + DeleteSurface(slab_surface[5]); +//graphics level icon + DeleteSurface(grfx_icon_surface[0]); + DeleteSurface(grfx_icon_surface[1]); + DeleteSurface(grfx_icon_surface[2]); + DeleteSurface(grfx_icon_surface[3]); +//reverse stereo icon + DeleteSurface(slab_surface[6]); + DeleteSurface(slab_surface[7]); + + Kill_chr_surfaces(); +} +//----------------------------------------------------------------------------------------------------------------------- +int Mouse_touching_button(int32 x, int32 y, int32 w, int32 h) //pete9Jun97 +{ + if ((mousex>x)&&(mousex<x+w)&&((mousey+40)>y)&&((mousey+40)<y+h)) + return (1); + else + return (0); +} + +//----------------------------------------------------------------------------------------------------------------------- +void Option_control(void) //Pete6Jun97 +{ +#define WORD_BUTTON_GAP 10 + +// some things left by the last tennant + uint8 black[4] = {0,0,0,0}; + char c; + _mouseEvent *me; + int first = 0; + +// text strings and lengths + uint8 title_text[MAX_STRING_LEN]; + uint8 subtitle_text[MAX_STRING_LEN]; + uint8 object_text[MAX_STRING_LEN]; + uint8 ok_text[MAX_STRING_LEN]; + uint8 cancel_text[MAX_STRING_LEN]; + uint8 music_text[MAX_STRING_LEN]; + uint8 speech_text[MAX_STRING_LEN]; + uint8 fx_text[MAX_STRING_LEN]; + uint8 graphics_text[MAX_STRING_LEN]; + uint8 stereo_text[MAX_STRING_LEN]; + uint8 *text; + int title_len, subtitle_len, ok_len, cancel_len, left_align, test_len; + +// slider values + uint8 musicVolume = GetMusicVolume(); + uint8 speechVolume = GetSpeechVolume(); + uint8 fxVolume = GetFxVolume(); + uint8 grfxLevel = GetRenderType(); + +// safe slider values for restoring on cancel + uint8 safe_musicVolume = musicVolume; + uint8 safe_speechVolume = speechVolume; + uint8 safe_fxVolume = fxVolume; + uint8 safe_grfxLevel = grfxLevel; + +// button state variables + uint8 dreverse_stereo_state = 0, dmusic_mute_state = 0, dspeech_mute_state = 0, dfx_mute_state = 0, dobject_state = 0, dsubtitle_state = 0; + uint8 touching_reverse_stereo, touching_music_mute, touching_fx_mute, touching_speech_mute, touching_object, touching_subtitle; + uint8 lb_down = 0; + +// Slider targets + uint8 music_target = musicVolume; + uint8 fx_target = fxVolume; + uint8 speech_target = speechVolume; + uint8 grfx_target = grfxLevel; + +// Slider movement types (click in track or drag button) + uint8 music_tracking = 0, fx_tracking = 0, speech_tracking = 0, grfx_tracking = 0; + +//do some driver stuff +// ResetRenderEngine(); + +// FETCH THE TEXT +//fetch the 'options' text + text = FetchTextLine( res_man.Res_open(149618698/SIZE), 149618698&0xffff ); //options (title) + strcpy((char*)title_text, (char*)text+2); + title_len = Pixel_text_length(title_text, controls_font_id); + +//fetch the 'subtitles' text + text = FetchTextLine( res_man.Res_open(149618699/SIZE), 149618699&0xffff ); //subtitles + strcpy((char*)subtitle_text, (char*)text+2); + subtitle_len = Pixel_text_length(subtitle_text, controls_font_id) + WORD_BUTTON_GAP; + +//fetch the 'object labels' text + text = FetchTextLine( res_man.Res_open(149618700/SIZE), 149618700&0xffff ); //object + strcpy((char*)object_text, (char*)text+2); + left_align = Pixel_text_length(object_text, controls_font_id) + WORD_BUTTON_GAP; + +//fetch the 'ok' text + text = FetchTextLine( res_man.Res_open(149618688/SIZE), 149618688&0xffff ); //ok + strcpy((char*)ok_text, (char*)text+2); + ok_len = Pixel_text_length(ok_text, controls_font_id) + WORD_BUTTON_GAP; + +//fetch the 'cancel' text + text = FetchTextLine( res_man.Res_open(149618689/SIZE), 149618689&0xffff ); //cancel + strcpy((char*)cancel_text, (char*)text+2); + cancel_len = Pixel_text_length(cancel_text, controls_font_id) + WORD_BUTTON_GAP; + +//fetch the 'music volume' text + text = FetchTextLine( res_man.Res_open(149618702/SIZE), 149618702&0xffff ); //music volume + strcpy((char*)music_text, (char*)text+2); + test_len = Pixel_text_length(music_text, controls_font_id) + WORD_BUTTON_GAP; + if (test_len>left_align) + left_align = test_len; + +//fetch the 'speech volume' text + text = FetchTextLine( res_man.Res_open(149618703/SIZE), 149618703&0xffff ); //speech volume + strcpy((char*)speech_text, (char*)text+2); + test_len = Pixel_text_length(speech_text, controls_font_id) + WORD_BUTTON_GAP; + if (test_len>left_align) + left_align = test_len; + +//fetch the 'fx volume' text + text = FetchTextLine( res_man.Res_open(149618704/SIZE), 149618704&0xffff ); //fx volume + strcpy((char*)fx_text, (char*)text+2); + test_len = Pixel_text_length(fx_text, controls_font_id) + WORD_BUTTON_GAP; + if (test_len>left_align) + left_align = test_len; + +//fetch the 'grapihics quality' text + text = FetchTextLine( res_man.Res_open(149618705/SIZE), 149618705&0xffff ); //graphics quality + strcpy((char*)graphics_text, (char*)text+2); + test_len = Pixel_text_length(graphics_text, controls_font_id) + WORD_BUTTON_GAP; + if (test_len>left_align) + left_align = test_len; + +//fetch the 'grapihics quality' text + text = FetchTextLine( res_man.Res_open(149618709/SIZE), 149618709&0xffff ); //graphics quality + strcpy((char*)stereo_text, (char*)text+2); + test_len = Pixel_text_length(stereo_text, controls_font_id) + WORD_BUTTON_GAP; + if (test_len>left_align) + left_align = test_len; + + +//blimey, life's never easy is it? +//not once you've got out of bed ! + + +//set the button states + restore_button_state = 0; + can_button_state = 0; + touching_object = 0; + touching_subtitle = 0; + uint8 object_state = pointerTextSelected; + uint8 subtitle_state = subtitles; + uint8 stereo_state = stereoReversed; + + uint8 music_mute_state = IsMusicMute(); + uint8 speech_mute_state = IsSpeechMute(); + uint8 fx_mute_state = IsFxMute(); + + +//build the button surfaces surfaces + Build_option_surfaces(); + +//position the sliders + slab_sprite[0].x = SLIDER_TRK_X + (SLIDER_TRK_W * musicVolume) / 16; + slab_sprite[1].x = SLIDER_TRK_X + (SLIDER_TRK_W * speechVolume) / 14; + slab_sprite[2].x = SLIDER_TRK_X + (SLIDER_TRK_W * fxVolume) / 14; + slab_sprite[3].x = SLIDER_TRK_X + (SLIDER_TRK_W * grfxLevel) / 3; + +//control loop + while (1) + { +// Update any moving sliders + // music + if (slab_sprite[0].x<SLIDER_TRK_X + (SLIDER_TRK_W * music_target) / 16) + { + if ((SLIDER_TRK_X + (SLIDER_TRK_W * music_target) / 16)-slab_sprite[0].x<2) + slab_sprite[0].x++; + else + slab_sprite[0].x +=2; + musicVolume = (int)((float)((slab_sprite[0].x-SLIDER_TRK_X)*16)/(float)SLIDER_TRK_W+0.5); + } + else if (slab_sprite[0].x>SLIDER_TRK_X + (SLIDER_TRK_W * music_target) / 16) + { + if (slab_sprite[0].x-(SLIDER_TRK_X + (SLIDER_TRK_W * music_target) / 16)<2) + slab_sprite[0].x--; + else + slab_sprite[0].x -=2; + musicVolume = (int)((float)((slab_sprite[0].x-SLIDER_TRK_X)*16)/(float)SLIDER_TRK_W+0.5); + + if (!musicVolume) + music_mute_state = 1; + else + music_mute_state = 0; + } + + // speech + if (slab_sprite[1].x<SLIDER_TRK_X + (SLIDER_TRK_W * speech_target) / 14) + { + if ((SLIDER_TRK_X + (SLIDER_TRK_W * speech_target) / 14)-slab_sprite[1].x<2) + slab_sprite[1].x++; + else + slab_sprite[1].x +=2; + speechVolume = (int)((float)((slab_sprite[1].x-SLIDER_TRK_X)*14)/(float)SLIDER_TRK_W+0.5); + } + else if (slab_sprite[1].x>SLIDER_TRK_X + (SLIDER_TRK_W * speech_target) / 14) + { + if (slab_sprite[1].x-(SLIDER_TRK_X + (SLIDER_TRK_W * speech_target) / 14)<2) + slab_sprite[1].x--; + else + slab_sprite[1].x -=2; + speechVolume = (int)((float)((slab_sprite[1].x-SLIDER_TRK_X)*14)/(float)SLIDER_TRK_W+0.5); + + if (!speechVolume) + speech_mute_state = 1; + else + speech_mute_state = 0; + } + + // fx + if (slab_sprite[2].x<SLIDER_TRK_X + (SLIDER_TRK_W * fx_target) / 14) + { + if ((SLIDER_TRK_X + (SLIDER_TRK_W * fx_target) / 14)-slab_sprite[2].x<2) + slab_sprite[2].x++; + else + slab_sprite[2].x +=2; + fxVolume = (int)((float)((slab_sprite[2].x-SLIDER_TRK_X)*14)/(float)SLIDER_TRK_W+0.5); + } + else if (slab_sprite[2].x>SLIDER_TRK_X + (SLIDER_TRK_W * fx_target) / 14) + { + if (slab_sprite[2].x-(SLIDER_TRK_X + (SLIDER_TRK_W * fx_target) / 14)<2) + slab_sprite[2].x--; + else + slab_sprite[2].x -=2; + fxVolume = (int)((float)((slab_sprite[2].x-SLIDER_TRK_X)*14)/(float)SLIDER_TRK_W+0.5); + + if (!fxVolume) + fx_mute_state = 1; + else + fx_mute_state = 0; + } + + // grfx + if (slab_sprite[3].x<SLIDER_TRK_X + (SLIDER_TRK_W * grfx_target) / 3) + { + if ((SLIDER_TRK_X + (SLIDER_TRK_W * grfx_target) / 3)-slab_sprite[3].x<2) + slab_sprite[3].x++; + else + slab_sprite[3].x +=2; + grfxLevel = (int)((float)((slab_sprite[3].x-SLIDER_TRK_X)*3)/(float)SLIDER_TRK_W+0.5); + } + else if (slab_sprite[3].x>SLIDER_TRK_X + (SLIDER_TRK_W * grfx_target) / 3) + { + if (slab_sprite[3].x-(SLIDER_TRK_X + (SLIDER_TRK_W * grfx_target) / 3)<2) + slab_sprite[3].x--; + else + slab_sprite[3].x -=2; + grfxLevel = (int)((float)((slab_sprite[3].x-SLIDER_TRK_X)*3)/(float)SLIDER_TRK_W+0.5); + } + + + if (music_tracking) // music tracking + { + slab_sprite[0].x = mousex - SLIDER_W/2; + if (slab_sprite[0].x < SLIDER_TRK_X) + slab_sprite[0].x = SLIDER_TRK_X; + else if (slab_sprite[0].x > SLIDER_TRK_X+SLIDER_TRK_W) + slab_sprite[0].x = SLIDER_TRK_X+SLIDER_TRK_W; + music_target = musicVolume = (int)((float)((slab_sprite[0].x-SLIDER_TRK_X)*16)/(float)SLIDER_TRK_W+0.5); + + if (!musicVolume) + music_mute_state = 1; + else + music_mute_state = 0; + + } + else if (speech_tracking) // speech tracking + { + slab_sprite[1].x = mousex - SLIDER_W/2; + if (slab_sprite[1].x < SLIDER_TRK_X) + slab_sprite[1].x = SLIDER_TRK_X; + else if (slab_sprite[1].x > SLIDER_TRK_X+SLIDER_TRK_W) + slab_sprite[1].x = SLIDER_TRK_X+SLIDER_TRK_W; + speech_target = speechVolume = (int)((float)((slab_sprite[1].x-SLIDER_TRK_X)*14)/(float)SLIDER_TRK_W+0.5); + + if (!speechVolume) + speech_mute_state = 1; + else + speech_mute_state = 0; + + } + else if (fx_tracking) // fx tracking + { + slab_sprite[2].x = mousex - SLIDER_W/2; + if (slab_sprite[2].x < SLIDER_TRK_X) + slab_sprite[2].x = SLIDER_TRK_X; + else if (slab_sprite[2].x > SLIDER_TRK_X+SLIDER_TRK_W) + slab_sprite[2].x = SLIDER_TRK_X+SLIDER_TRK_W; + fx_target = fxVolume = (int)((float)((slab_sprite[2].x-SLIDER_TRK_X)*14)/(float)SLIDER_TRK_W+0.5); + + if (!fxVolume) + fx_mute_state = 1; + else + fx_mute_state = 0; + + } + else if (grfx_tracking) // grfx tracking + { + slab_sprite[3].x = mousex - SLIDER_W/2; + if (slab_sprite[3].x < SLIDER_TRK_X) + slab_sprite[3].x = SLIDER_TRK_X; + else if (slab_sprite[3].x > SLIDER_TRK_X+SLIDER_TRK_W) + slab_sprite[3].x = SLIDER_TRK_X+SLIDER_TRK_W; + grfx_target = grfxLevel = (int)((float)((slab_sprite[3].x-SLIDER_TRK_X)*3)/(float)SLIDER_TRK_W+0.5); + } + + if (!music_mute_state) + SetMusicVolume(musicVolume); + else + SetMusicVolume(0); + + if (!fx_mute_state) + SetFxVolume(fxVolume); + else + SetFxVolume(0); + + if (!speech_mute_state) + SetSpeechVolume(speechVolume); + else + SetSpeechVolume(0); + + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q during the smacker + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + //-------------------------------------------------- + + EraseBackBuffer(); + + +//print panel + while (DrawSurface(&panel_sprite, panel_surface)==RDERR_SURFACELOST) + { + Kill_option_surfaces(); + Build_option_surfaces(); + }; + +//print words on panel option panel + Engine_string(OPTION_W/2-(title_len/2)+OPTION_X,OPTION_Y+15, controls_font_id, chr_surface, title_text); //options + Engine_string(SUBTITLE_X-subtitle_len, SUBTITLE_Y+3, controls_font_id, chr_surface, subtitle_text); //subtitles + Engine_string(SLIDER_TRK_X-left_align, OBJ_LABEL_Y+3, controls_font_id, chr_surface, object_text); //object labels + Engine_string(OPT_OK_X-ok_len, OPT_OK_Y, controls_font_id, chr_surface, ok_text); //ok + Engine_string(OPT_CAN_X-cancel_len, OPT_CAN_Y, controls_font_id, chr_surface, cancel_text); //cancel + Engine_string(SLIDER_TRK_X-left_align, MUSIC_TRK_Y, controls_font_id, chr_surface, music_text); //music volume + Engine_string(SLIDER_TRK_X-left_align, SPEECH_TRK_Y, controls_font_id, chr_surface, speech_text); //speech volume + Engine_string(SLIDER_TRK_X-left_align, FX_TRK_Y, controls_font_id, chr_surface, fx_text); //fx volume + Engine_string(SLIDER_TRK_X-left_align, GRFX_TRK_Y, controls_font_id, chr_surface, graphics_text); //graphics quality + Engine_string(SLIDER_TRK_X-left_align, STEREO_Y+3, controls_font_id, chr_surface, stereo_text); //reverse stereo + +//print buttons + DrawSurface(&down_button_sprite[subtitle_state], down_button_surface[subtitle_state] ); //print subtitles button + + DrawSurface(&up_button_sprite[object_state], up_button_surface[object_state] ); //print object labels button + + DrawSurface(&button_sprite[restore_button_state], button_surface[restore_button_state] ); //print ok button + + DrawSurface(&can_button_sprite[can_button_state], can_button_surface[can_button_state] ); //print cancel button + + DrawSurface(&slab_sprite[0], slab_surface[0]); //print sliders + DrawSurface(&slab_sprite[1], slab_surface[1]); + DrawSurface(&slab_sprite[2], slab_surface[2]); + DrawSurface(&slab_sprite[3], slab_surface[3]); + + DrawSurface(&zup_button_sprite[music_mute_state], zup_button_surface[music_mute_state] ); //print mute buttons + DrawSurface(&zdown_button_sprite[speech_mute_state], zdown_button_surface[speech_mute_state] ); + DrawSurface(&slab_sprite[fx_mute_state+4], slab_surface[fx_mute_state+4] ); + + DrawSurface(&grfx_icon_sprite[grfxLevel], grfx_icon_surface[grfxLevel] ); //print the graphics level icon + + DrawSurface(&slab_sprite[6+stereo_state], slab_surface[6+stereo_state]); // print reverse stereo button + +//keep menu up too + ProcessMenu(); + +//user can ESC quit + if (KeyWaiting()) + { + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + { + ReadOptionSettings(); // Reset options to previous settings. + break; + } + } + +// check what if anything the mouse is touching +//mouse over ok button? + if (Mouse_touching_button(OPT_OK_X,OPT_OK_Y,OPT_BUT_W,OPT_BUT_H)) + touching_restore_button=1; //mouse over button + else //not over so release even if pressed previously + { restore_button_state=0; + touching_restore_button=0; + } + +//mouse over cancel button? + if (Mouse_touching_button(OPT_CAN_X,OPT_CAN_Y,OPT_BUT_W,OPT_BUT_H)) + touching_can_button=1; //mouse over button + else //not over so release even if pressed previously + { can_button_state=0; + touching_can_button=0; + } + +//mouse over object label button? + if (Mouse_touching_button(OBJ_LABEL_X,OBJ_LABEL_Y,OPT_BUT_W,OPT_BUT_H)) + { + if (!lb_down) + touching_object=1; //mouse over button + } + else //not over so release even if pressed previously + { + if (touching_object && lb_down && !dobject_state) + object_state=!object_state; + touching_object=0; + } + +//mouse over subtitles button? + if (Mouse_touching_button(SUBTITLE_X,SUBTITLE_Y,OPT_BUT_W,OPT_BUT_H)) + { + if (!lb_down) + touching_subtitle=1; //mouse over button + } + else //not over so release even if pressed previously + { + if (touching_subtitle && lb_down && !dsubtitle_state) + subtitle_state=!subtitle_state; + touching_subtitle=0; + } + +//mouse over reverse stereo button? + if (Mouse_touching_button(STEREO_X,STEREO_Y,OPT_BUT_W,OPT_BUT_H)) + { + if (!lb_down) + touching_reverse_stereo=1; //mouse over button + } + else //not over so release even if pressed previously + { + if (touching_reverse_stereo && lb_down && !dreverse_stereo_state) + stereo_state=!stereo_state; + touching_reverse_stereo=0; + } + +//mouse over music mute button? + if (Mouse_touching_button(MUTE_X,MUSIC_TRK_Y-4,MUTE_W,MUTE_H)) + { + if (!lb_down) + touching_music_mute=1; //mouse over button + } + else //not over so release even if pressed previously + { + if (touching_music_mute && lb_down && !dmusic_mute_state) + music_mute_state=!music_mute_state; + touching_music_mute=0; + } + +//mouse over fx mute button? + if (Mouse_touching_button(MUTE_X,FX_TRK_Y-4,MUTE_W,MUTE_H)) + { + if (!lb_down) + touching_fx_mute=1; //mouse over button + } + else //not over so release even if pressed previously + { + if (touching_fx_mute && lb_down && !dfx_mute_state) + fx_mute_state=!fx_mute_state; + touching_fx_mute=0; + } + +//mouse over speech mute button? + if (Mouse_touching_button(MUTE_X,SPEECH_TRK_Y-4,MUTE_W,MUTE_H)) + { + if (!lb_down) + touching_speech_mute=1; //mouse over button + } + else //not over so release even if pressed previously + { + if (touching_speech_mute && lb_down && !dspeech_mute_state) + speech_mute_state=!speech_mute_state; + touching_speech_mute=0; + } + + +//pressing on a button + me = MouseEvent(); //get mouse event + + if (me!=NULL) + { + if (me->buttons&RD_LEFTBUTTONUP) + { + lb_down = 0; + if (touching_restore_button && restore_button_state) // ok to settings + { + UpdateGraphicsLevel(safe_grfxLevel, grfxLevel); // (James13jun97) + + MuteMusic(music_mute_state); // Ensure all the levels are recorded correctly (Pete21Aug97) + MuteSpeech(speech_mute_state); + MuteFx(fx_mute_state); + SetMusicVolume(music_target); + SetSpeechVolume(speech_target); + SetFxVolume(fx_target); + + subtitles = subtitle_state; // Save object label and subtitle settings + pointerTextSelected = object_state; + speechSelected = !speech_mute_state; + + if (stereo_state != stereoReversed) + ReverseStereo(); + + stereoReversed = stereo_state; + WriteOptionSettings(); + break; + } + + if (touching_can_button && can_button_state) // cancel, so restore old settings + { + ReadOptionSettings(); + break; + } + + if (touching_object && dobject_state) + dobject_state = object_state=0; // if the button was in now let it out + + if (touching_subtitle && dsubtitle_state) + subtitle_state = dsubtitle_state = 0; // if the button was in now let it out + + if (touching_reverse_stereo && dreverse_stereo_state) + dreverse_stereo_state = stereo_state = 0; // if the button was in now let it out + + if (touching_music_mute && dmusic_mute_state) { + music_mute_state = dmusic_mute_state = 0; // if the button was in now let it out + MuteMusic(0); + } + + if (touching_fx_mute && dfx_mute_state) { + fx_mute_state = dfx_mute_state = 0; // if the button was in now let it out + MuteFx(0); + } + + if (touching_speech_mute && dspeech_mute_state) { + speech_mute_state = dspeech_mute_state = 0; // if the button was in now let it out + MuteSpeech(0); + } + + // Stop tracking any sliders + music_tracking = fx_tracking = speech_tracking = grfx_tracking = 0; + } + + else if (me->buttons&RD_LEFTBUTTONDOWN) //there's a mouse event to be processed + { + lb_down = 1; + if (touching_restore_button) + restore_button_state=1; + + if (touching_can_button) + can_button_state=1; + + if (touching_object) + { + if (object_state) // push in the button if it's out + dobject_state = 1; + else + object_state=!object_state; + } + + if (touching_subtitle) + { + if (subtitle_state) + dsubtitle_state = 1; + else + subtitle_state=!subtitle_state; + } + + if (touching_reverse_stereo) + { + if (stereo_state) // push in the button if it's out + dreverse_stereo_state = 1; + else + stereo_state = !stereo_state; + } + + + if (touching_music_mute) + { + if (music_mute_state) + dmusic_mute_state = 1; + else + { + music_mute_state = 1; + MuteMusic(1); + } + } + + if (touching_fx_mute) + { + if (fx_mute_state) + dfx_mute_state = 1; + else + { + fx_mute_state=1; + MuteFx(1); + } + } + + if (touching_speech_mute) + { + if (speech_mute_state) + dspeech_mute_state = 1; + else + { + speech_mute_state=1; + MuteSpeech(1); + } + } + + if (Mouse_touching_button(SLIDER_TRK_X,MUSIC_TRK_Y,SLIDER_TRK_W+SLIDER_W,SLIDER_TRK_H)) + { + if (music_mute_state) + { + music_mute_state = 0; + MuteMusic(0); + } + + if (mousex>(slab_sprite[0].x+SLIDER_W)) + { + if (music_target<musicVolume) + music_target = musicVolume; + music_target++; + if (music_target>15) + music_target = 15; + } + else if (mousex<slab_sprite[0].x) + { + if (music_target>musicVolume) + music_target = musicVolume; + music_target--; + if (music_target>15) + music_target = 0; + } else + music_tracking = 1; + } + + if (Mouse_touching_button(SLIDER_TRK_X,SPEECH_TRK_Y,SLIDER_TRK_W+SLIDER_W,SLIDER_TRK_H)) + { + if (speech_mute_state) + { + speech_mute_state = 0; + MuteSpeech(0); + } + + if (mousex>(slab_sprite[1].x+SLIDER_W)) + { + if (speech_target<speechVolume) + speech_target = speechVolume; + speech_target++; + if (speech_target>14) + speech_target = 14; + } + else if (mousex<slab_sprite[1].x) + { + if (speech_target>speechVolume) + speech_target = speechVolume; + speech_target--; + if (speech_target>14) + speech_target = 0; + } else + speech_tracking = 1; + } + + if (Mouse_touching_button(SLIDER_TRK_X,FX_TRK_Y,SLIDER_TRK_W+SLIDER_W,SLIDER_TRK_H)) + { + if (fx_mute_state) + { + fx_mute_state = 0; + MuteFx(0); + } + + if (mousex>(slab_sprite[2].x+SLIDER_W)) + { + if (fx_target<fxVolume) + fx_target = fxVolume; + fx_target++; + if (fx_target>14) + fx_target = 14; + } + else if (mousex<slab_sprite[2].x) + { + if (fx_target>fxVolume) + fx_target = fxVolume; + fx_target--; + if (fx_target>14) + fx_target = 0; + } + else + fx_tracking = 1; + + fx_mute_state = 0; + } + + if (Mouse_touching_button(SLIDER_TRK_X,GRFX_TRK_Y,SLIDER_TRK_W+SLIDER_W,SLIDER_TRK_H)) + { + if (mousex>(slab_sprite[3].x+SLIDER_W)) + { + if (grfx_target<grfxLevel) + grfx_target = grfxLevel; + grfx_target++; + if (grfx_target>3) + grfx_target = 3; + } + else if (mousex<slab_sprite[3].x) + { + if (grfx_target>grfxLevel) + grfx_target = grfxLevel; + grfx_target--; + if (grfx_target>3) + grfx_target = 0; + } + else + grfx_tracking = 1; + } + } + } + + + if (!first) + { + first++; + SetFullPalette(CONTROL_PANEL_PALETTE); // see Build_display.cpp (James17jun97) + } + FlipScreens(); + } + + + Kill_option_surfaces(); + + return; //just return to game +} + +//----------------------------------------------------------------------------------------------------------------------- +void UpdateGraphicsLevel(uint8 oldLevel, uint8 newLevel) // (James13jun97) +{ + + switch (oldLevel) // Set the graphics level + { + //------------------------------- + case 0: // lowest setting: h/w only; no graphics fx + switch(newLevel) + { + case 0: + break; + + case 1: + RenderSoft(); + ClearBltFx(); + ClearShadowFx(); + CloseBackgroundLayer(); + break; + + case 2: + RenderSoft(); + ClearBltFx(); + CloseBackgroundLayer(); + break; + + case 3: // same as case 2 until case 2 has edge-blending inactivated + RenderSoft(); + CloseBackgroundLayer(); + break; + } + break; + //------------------------------- + case 1: // medium-low setting: s/w transparency-blending + switch(newLevel) + { + case 1: + break; + + case 0: + RenderHard(); + SetUpBackgroundLayers(); // InitialiseBackgroundLayer for each layer! (see layers.cpp) + break; + + case 2: + SetShadowFx(); + break; + + case 3: // same as case 2 until case 2 has edge-blending inactivated + SetBltFx(); + break; + } + break; + //------------------------------- + case 2: // medium-high setting: s/w transparency-blending + shading + switch(newLevel) + { + case 2: + break; + + case 3: // same as case 2 until case 2 has edge-blending inactivated + SetBltFx(); + break; + + case 1: + ClearShadowFx(); + break; + + case 0: + RenderHard(); + SetUpBackgroundLayers(); // InitialiseBackgroundLayer for each layer! (see layers.cpp) + break; + } + break; + //------------------------------- + case 3: // highest setting: s/w transparency-blending + shading + edge-blending (& improved stretching) + switch(newLevel) + { + case 2: + ClearBltFx(); + break; + + case 3: // same as case 2 until case 2 has edge-blending inactivated + break; + + case 1: + ClearBltFx(); + ClearShadowFx(); + break; + + case 0: + RenderHard(); + SetUpBackgroundLayers(); // InitialiseBackgroundLayer for each layer! (see layers.cpp) + break; + } + break; + //------------------------------- + } + + // update our global variable - which needs to be checked when dimming the palette + // in PauseGame() in sword2.cpp (since palette-matching cannot be done with dimmed palette + // so we turn down one notch while dimmed, if at top level) + current_graphics_level = newLevel; +} +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- + diff --git a/sword2/controls.h b/sword2/controls.h new file mode 100644 index 0000000000..95045e2e63 --- /dev/null +++ b/sword2/controls.h @@ -0,0 +1,38 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _CONTROL_S +#define _CONTROL_S + +#include "common/scummsys.h" +//#include "src\driver96.h" + +uint32 Restore_control(void); //Tony20Mar97 +void Save_control(void); //Tony1Apr97 +void Quit_control(void); //Tony2Apr97 +void Restart_control(void); //Tony4Apr97 +void Option_control(void); //Pete5Jun97 +int32 ReadOptionSettings(void); //Pete10Jun97 +void UpdateGraphicsLevel(uint8 oldLevel, uint8 newLevel); // (James13jun97) + +extern uint8 subtitles; // text selected +extern uint8 speechSelected; +extern uint8 current_graphics_level; + +#endif diff --git a/sword2/credits.h b/sword2/credits.h new file mode 100644 index 0000000000..26337c81c6 --- /dev/null +++ b/sword2/credits.h @@ -0,0 +1,28 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _Credits_h_ +#define _Credits_h_ + +#include "driver/driver96.h" + +// int32 __declspec( dllexport ) Credits(_drvDrawStatus *pDrawStatus, _drvSoundStatus *pSoundStatus, const char *cdPath, BOOL smoke, BOOL *pAppFocus, _drvKeyStatus *pKeyStatus); +int32 Credits(_drvDrawStatus *pDrawStatus, _drvSoundStatus *pSoundStatus, const char *cdPath, BOOL smoke, BOOL *pAppFocus, _drvKeyStatus *pKeyStatus); + +#endif diff --git a/sword2/debug.cpp b/sword2/debug.cpp new file mode 100644 index 0000000000..c7f1010140 --- /dev/null +++ b/sword2/debug.cpp @@ -0,0 +1,553 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//-------------------------------------------------------------------------------------- +#include <stdarg.h> // for ExitWithReport, which stays in RELEASE version +#include <stdio.h> + +#include "driver/driver96.h" +#include "debug.h" +//-------------------------------------------------------------------------------------- + +#if _DEBUG // this whole file (except ExitWithReport) only included on debug versions + +#include <stdlib.h> + +#include "build_display.h" // for 'fps' (frames-per-second counter) +#include "console.h" +#include "defs.h" +#include "events.h" // for CountEvents() +#include "layers.h" +#include "logic.h" +#include "maketext.h" +#include "mem_view.h" +#include "mouse.h" +#include "protocol.h" +#include "resman.h" +#include "router.h" // for PlotWalkGrid() +#include "speech.h" // for 'officialTextNumber' and 'speechScriptWaiting' + +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +// global variables +uint8 displayDebugText = 0; // "INFO" 0=off; 1=on +uint8 displayWalkGrid = 0; // "WALKGRID" +uint8 displayMouseMarker = 0; // "MOUSE" +uint8 displayTime = 0; // "TIME" +uint8 displayPlayerMarker = 0; // "PLAYER" +uint8 displayTextNumbers = 0; // "TEXT" +uint8 renderSkip = 0; // Toggled on 'S' key - to render only 1 in 4 frames, to speed up game + +uint8 definingRectangles = 0; // "RECT" +uint8 draggingRectangle = 0; // 0=waiting to start new rect; 1=currently dragging a rectangle +int16 rect_x1 = 0; +int16 rect_y1 = 0; +int16 rect_x2 = 0; +int16 rect_y2 = 0; +uint8 rectFlicker=0; + +uint8 testingSnR = 0; // "SAVEREST" - for system to kill all object resources (except player) in FN_add_human() + +int32 startTime = 0; // "TIMEON" & "TIMEOFF" - system start time. +int32 gameCycle = 0; // Counter for game clocks. + +int32 textNumber = 0; // current system text line number + +int32 showVar[MAX_SHOWVARS]; // "SHOWVAR" + +Object_graphic playerGraphic; // for displaying player object's current graphical info +uint32 player_graphic_no_frames=0; // no. of frames in currently displayed anim + +uint8 debug_text_blocks[MAX_DEBUG_TEXT_BLOCKS]; + +//-------------------------------------------------------------------------------------- +// function prototypes + +void Clear_debug_text_blocks( void ); +void Make_debug_text_block( char *text, int16 x, int16 y ); +void Plot_cross_hair( int16 x, int16 y, uint8 pen ); +void DrawRect( int16 x, int16 y, int16 x2, int16 y2, uint8 pen ); +//-------------------------------------------------------------------------------------- +#endif // _DEBUG + +// THIS FUNCTION STAYS IN THE RELEASE VERSION +// IN FACT, CON_FATAL_ERROR IS MAPPED TO THIS AS WELL, SO WE HAVE A MORE PRESENTABLE ERROR REPORT +void ExitWithReport(char *format,...) // (6dec96 JEL) +{ + // Send a printf type string to Paul's windows routine + char buf[500]; + va_list arg_ptr; // Variable argument pointer + + va_start(arg_ptr,format); + + + vsprintf(buf, format, arg_ptr); + Zdebug("%s",buf); // send output to 'debug.txt' as well, just for the record + + while (GetFadeStatus()) // wait for fade to finish before calling RestoreDisplay() + ServiceWindows(); + + RestoreDisplay(); + ReportFatalError((uint8 *)buf); // display message box + CloseAppWindow(); + while (ServiceWindows() != RDERR_APPCLOSED); + + exit(0); +} + +#if _DEBUG // all other functions only for _DEBUG version +//-------------------------------------------------------------------------------------- +void Zdebug(char *format,...) //Tony's special debug logging file March96 +{ +// Write a printf type string to a debug file + + va_list arg_ptr; // Variable argument pointer + FILE * debug_filep=0; // Debug file pointer + static int first_debug = 1; // Flag for first time this is used + + va_start(arg_ptr,format); + + if (first_debug) //First time round delete any previous debug file + { + unlink("debug.txt"); + first_debug = 0; + } + + debug_filep = fopen("debug.txt","a+t"); + + if (debug_filep != NULL) // if it could be opened + { + vfprintf(debug_filep, format, arg_ptr); + fprintf(debug_filep,"\n"); + + fclose(debug_filep); + } +} + +//-------------------------------------------------------------------------------------- +void Zdebug(uint32 stream, char *format,...) //Tony's special debug logging file March96 +{ +// Write a printf type string to a debug file + + va_list arg_ptr; // Variable argument pointer + FILE * debug_filep=0; // Debug file pointer + static int first = 1; // Flag for first time this is used + int j; + static int first_debugs[100]; + + + + if (first==1) //first time run then reset the states + { for (j=0;j<100;j++) + first_debugs[j]=0; + + first=0; + } + + + + + char name[20]; + + + sprintf(name, "debug%d.txt", stream); + + va_start(arg_ptr,format); + + if (!first_debugs[stream]) //First time round delete any previous debug file + { + unlink(name); + first_debugs[stream] = 1; + } + + debug_filep = fopen(name,"a+t"); + + if (debug_filep != NULL) // if it could be opened + { + vfprintf(debug_filep, format, arg_ptr); + fprintf(debug_filep,"\n"); + + fclose(debug_filep); + } +} +//-------------------------------------------------------------------------------------- +void Clear_debug_text_blocks( void ) // JAMES +{ + uint8 blockNo=0; + + + while ((blockNo < MAX_DEBUG_TEXT_BLOCKS) && (debug_text_blocks[blockNo] > 0)) + { + Kill_text_bloc(debug_text_blocks[blockNo]); // kill the system text block + debug_text_blocks[blockNo] = 0; // clear this element of our array of block numbers + blockNo++; + } +} +//-------------------------------------------------------------------------------------- +void Make_debug_text_block( char *text, int16 x, int16 y) // JAMES +{ + uint8 blockNo=0; + + + while ((blockNo < MAX_DEBUG_TEXT_BLOCKS) && (debug_text_blocks[blockNo] > 0)) + blockNo++; + + if (blockNo == MAX_DEBUG_TEXT_BLOCKS) + Con_fatal_error("ERROR: debug_text_blocks[] full in Make_debug_text_block() at line %d in file \"%s\"",__LINE__,__FILE__); + + debug_text_blocks[blockNo] = Build_new_block( (uint8 *)text, x, y, 640-x, 0, RDSPR_DISPLAYALIGN, CONSOLE_FONT_ID, NO_JUSTIFICATION); +} + +//-------------------------------------------------------------------------------------- +// +// +// PC Build_debug_info +// +// +//-------------------------------------------------------------------------------------- +void Build_debug_text( void ) // JAMES +{ + char buf[128]; + + int32 showVarNo; // for variable watching + int32 showVarPos; + int32 varNo; + int32 *varTable; + + + Clear_debug_text_blocks(); // clear the array of text block numbers for the debug text + + //------------------------------------------------------------------- + // mouse coords +/* + if (displayMouseMarker) // print mouse coords beside mouse-marker, if it's being displayed + { + sprintf (buf, "%d,%d", mousex+this_screen.scroll_offset_x, mousey+this_screen.scroll_offset_y); + if (mousex>560) + Make_debug_text_block (buf, mousex-50, mousey-15); + else + Make_debug_text_block (buf, mousex+5, mousey-15); + } +*/ + //------------------------------------------------------------------- + // mouse area coords + + if (draggingRectangle || SYSTEM_TESTING_ANIMS) // defining a mouse area the easy way, by creating a box on-screen + { + rectFlicker = 1-rectFlicker; // so we can see what's behind the lines + + sprintf (buf, "x1=%d", rect_x1); + Make_debug_text_block (buf, 0, 120); + + sprintf (buf, "y1=%d", rect_y1); + Make_debug_text_block (buf, 0, 135); + + sprintf (buf, "x2=%d", rect_x2); + Make_debug_text_block (buf, 0, 150); + + sprintf (buf, "y2=%d", rect_y2); + Make_debug_text_block (buf, 0, 165); + } + //------------------------------------------------------------------- + // testingSnR indicator + + if (testingSnR) // see FN_add_human() + { + sprintf (buf, "TESTING LOGIC STABILITY!"); + Make_debug_text_block (buf, 0, 105); + } + //--------------------------------------------- + // speed-up indicator + + if (renderSkip) // see sword.cpp + { + sprintf (buf, "SKIPPING FRAMES FOR SPEED-UP!"); + Make_debug_text_block (buf, 0, 120); + } + //--------------------------------------------- + // debug info at top of screen - enabled/disabled as one complete unit + + if (displayTime) + { + int32 time = timeGetTime(); + + if ((time - startTime) / 1000 >= 10000) + startTime = time; + + time -= startTime; + sprintf(buf, "Time %.2d:%.2d:%.2d.%.3d",(time / 3600000) % 60,(time / 60000) % 60, (time / 1000) % 60,time%1000); + Make_debug_text_block(buf, 500, 360); + sprintf(buf, "Game %d", gameCycle); + Make_debug_text_block(buf, 500, 380); + } + //--------------------------------------------- + // current text number & speech-sample resource id + + if (displayTextNumbers) + { + if (textNumber) + { + if (SYSTEM_TESTING_TEXT) + { + if (SYSTEM_WANT_PREVIOUS_LINE) + sprintf (buf, "backwards"); + else + sprintf (buf, "forwards"); + + Make_debug_text_block (buf, 0, 340); + } + + sprintf (buf, "res: %d", textNumber/SIZE); + Make_debug_text_block (buf, 0, 355); + + sprintf (buf, "pos: %d", textNumber&0xffff); + Make_debug_text_block (buf, 0, 370); + + sprintf (buf, "TEXT: %d", officialTextNumber); + Make_debug_text_block (buf, 0, 385); + + } + } + //--------------------------------------------- + // resource number currently being checking for animation + + if (SYSTEM_TESTING_ANIMS) + { + sprintf (buf, "trying resource %d", SYSTEM_TESTING_ANIMS); + Make_debug_text_block (buf, 0, 90); + } + //--------------------------------------------- + + // general debug info + + if (displayDebugText) + { + //--------------------------------------------- +/* + // CD in use + sprintf (buf, "CD-%d", currentCD); + Make_debug_text_block (buf, 0, 0); +*/ + //--------------------------------------------- + // mouse coords & object pointed to + + if (CLICKED_ID) + sprintf (buf, "last click at %d,%d (id %d: %s)", MOUSE_X, MOUSE_Y, CLICKED_ID, FetchObjectName(CLICKED_ID)); + else + sprintf (buf, "last click at %d,%d (---)", MOUSE_X, MOUSE_Y); + + Make_debug_text_block (buf, 0, 15); + + if (mouse_touching) + sprintf (buf, "mouse %d,%d (id %d: %s)", mousex+this_screen.scroll_offset_x, mousey+this_screen.scroll_offset_y, mouse_touching, FetchObjectName(mouse_touching)); + else + sprintf (buf, "mouse %d,%d (not touching)", mousex+this_screen.scroll_offset_x, mousey+this_screen.scroll_offset_y); + + Make_debug_text_block (buf, 0, 30); + + //--------------------------------------------- + // player coords & graphic info + + if (playerGraphic.anim_resource) // if player objct has a graphic + sprintf (buf, "player %d,%d %s (%d) #%d/%d", this_screen.player_feet_x, this_screen.player_feet_y, FetchObjectName(playerGraphic.anim_resource), playerGraphic.anim_resource, playerGraphic.anim_pc, player_graphic_no_frames); + else + sprintf (buf, "player %d,%d --- %d", this_screen.player_feet_x, this_screen.player_feet_y, playerGraphic.anim_pc); + + Make_debug_text_block (buf, 0, 45); + + //--------------------------------------------- + // frames-per-second counter + + sprintf (buf, "fps %d", fps); + Make_debug_text_block (buf, 440, 0); + + //--------------------------------------------- + // location number + + sprintf (buf, "location=%d", LOCATION); + Make_debug_text_block (buf, 440, 15); + + //--------------------------------------------- + // "result" variable + + sprintf (buf, "result=%d", RESULT); + Make_debug_text_block (buf, 440, 30); + + //--------------------------------------------- + // no. of events in event list + + sprintf (buf, "events=%d", CountEvents()); + Make_debug_text_block (buf, 440, 45); + + //--------------------------------------------- + // sprite list usage + + sprintf (buf, "bgp0: %d/%d",cur_bgp0,MAX_bgp0_sprites); + Make_debug_text_block (buf, 560, 0); + + sprintf (buf, "bgp1: %d/%d",cur_bgp1,MAX_bgp1_sprites); + Make_debug_text_block (buf, 560, 15); + + sprintf (buf, "back: %d/%d",cur_back,MAX_back_sprites); + Make_debug_text_block (buf, 560, 30); + + sprintf (buf, "sort: %d/%d",cur_sort,MAX_sort_sprites); + Make_debug_text_block (buf, 560, 45); + + sprintf (buf, "fore: %d/%d",cur_fore,MAX_fore_sprites); + Make_debug_text_block (buf, 560, 60); + + sprintf (buf, "fgp0: %d/%d",cur_fgp0,MAX_fgp0_sprites); + Make_debug_text_block (buf, 560, 75); + + sprintf (buf, "fgp1: %d/%d",cur_fgp1,MAX_fgp1_sprites); + Make_debug_text_block (buf, 560, 90); + + //--------------------------------------------- + // largest layer & sprite + + // NB. Strings already constructed in Build_display.cpp + Make_debug_text_block (largest_layer_info, 0, 60); + Make_debug_text_block (largest_sprite_info, 0, 75); + + //--------------------------------------------- + // "waiting for person" indicator - set form FN_they_do & FN_they_do_we_wait + + if (speechScriptWaiting) + { + sprintf (buf, "script waiting for %s (%d)", FetchObjectName(speechScriptWaiting), speechScriptWaiting); + Make_debug_text_block (buf, 0, 90); + } + //--------------------------------------------- + // variable watch display + + showVarPos = 115; // y-coord for first showVar + + varTable = (int32*)(res_man.Res_open(1) + sizeof(_standardHeader)); // res 1 is the global variables resource + + for (showVarNo=0; showVarNo < MAX_SHOWVARS; showVarNo++) + { + varNo = showVar[showVarNo]; // get variable number + + if (varNo) // if non-zero ie. cannot watch 'id' but not needed anyway because it changes throughout the logic loop + { + sprintf (buf, "var(%d) = %d", varNo, varTable[varNo]); + Make_debug_text_block (buf, 530, showVarPos); + showVarPos += 15; // next line down + } + } + + res_man.Res_close(1); // close global variables resource + + //--------------------------------------------- + // memory indicator - this should come last, to show all the sprite blocks above! + + Create_mem_string (buf); + Make_debug_text_block (buf, 0, 0); + + //--------------------------------------------- + } + + //------------------------------------------------------------------- + + +} + +//-------------------------------------------------------------------------------------- +void Draw_debug_graphics( void ) // JAMES (08apr97) +{ + //------------------------------- + // walk-grid + + if (displayWalkGrid) + PlotWalkGrid(); + + //------------------------------- + // player feet coord marker + + if (displayPlayerMarker) + Plot_cross_hair (this_screen.player_feet_x, this_screen.player_feet_y, 215); + + //------------------------------------------------------------------- + // mouse marker & coords + + if (displayMouseMarker) + Plot_cross_hair (mousex+this_screen.scroll_offset_x, mousey+this_screen.scroll_offset_y, 215); + + //------------------------------------------------------------------- + // mouse area rectangle / sprite box rectangle when testing anims + + if (SYSTEM_TESTING_ANIMS) + { + DrawRect(rect_x1, rect_y1, rect_x2, rect_y2, 184); // draw box around current frame + } + else if (draggingRectangle) // defining a mouse area the easy way, by creating a box on-screen + { + if (rectFlicker) + DrawRect(rect_x1, rect_y1, rect_x2, rect_y2, 184); + } + //------------------------------------------------------------------- +} +//-------------------------------------------------------------------------------------- +void Plot_cross_hair( int16 x, int16 y, uint8 pen ) +{ + PlotPoint(x,y,pen); // driver function + + DrawLine(x-2,y,x-5,y,pen); // driver function + DrawLine(x+2,y,x+5,y,pen); + + DrawLine(x,y-2,x,y-5,pen); + DrawLine(x,y+2,x,y+5,pen); +} +//-------------------------------------------------------------------------------------- +void DrawRect( int16 x1, int16 y1, int16 x2, int16 y2, uint8 pen ) +{ + DrawLine(x1,y1,x2,y1,pen); // top edge + DrawLine(x1,y2,x2,y2,pen); // bottom edge + DrawLine(x1,y1,x1,y2,pen); // left edge + DrawLine(x2,y1,x2,y2,pen); // right edge +} +//-------------------------------------------------------------------------------------- +void Print_current_info(void) //Tony30Oct96 +{ +//prints general stuff about the screen, etc. + + + if (this_screen.background_layer_id) + { Print_to_console(" background layer id %d", this_screen.background_layer_id); + Print_to_console(" %d wide, %d high", this_screen.screen_wide, this_screen.screen_deep); + Print_to_console(" %d normal layers", this_screen.number_of_layers); + + LLogic.Examine_run_list(); + + } + else + Print_to_console(" no screen"); + + + Scroll_console(); +} +//-------------------------------------------------------------------------------------- +#else // not debug + +void Draw_debug_graphics(void) {}; + +#endif // _DEBUG +//-------------------------------------------------------------------------------------- diff --git a/sword2/debug.h b/sword2/debug.h new file mode 100644 index 0000000000..0ecca67830 --- /dev/null +++ b/sword2/debug.h @@ -0,0 +1,92 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef D_DEBUG +#define D_DEBUG + +//-------------------------------------------------------------------------------------- +#ifdef _DEBUG // this whole file only included on debug versions + + +//#include "src\driver96.h" +#include "driver.h" +#include "object.h" + +#define MAX_DEBUG_TEXT_BLOCKS 50 + +extern uint8 displayDebugText; // 0=off; 1=on +extern uint8 displayWalkGrid; +extern uint8 displayMouseMarker; +extern uint8 displayPlayerMarker; +extern uint8 displayTime; +extern uint8 displayTextNumbers; +extern uint8 definingRectangles; +extern uint8 draggingRectangle; +extern uint8 displayTime; +extern int32 startTime; +extern int32 gameCycle; +extern uint8 renderSkip; + +extern int16 rect_x1; +extern int16 rect_y1; +extern int16 rect_x2; +extern int16 rect_y2; + +extern uint8 testingSnR; + +extern int32 textNumber; + +extern Object_graphic playerGraphic; +extern uint32 player_graphic_no_frames; + + + +#define MAX_SHOWVARS 15 +extern int32 showVar[MAX_SHOWVARS]; + + +void Zdebug(char * ,...); // Tony's special debug logging file March96 +void Zdebug(uint32 stream, char *format,...); +void Build_debug_text(void); // James's debug text display +void Draw_debug_graphics(void); // James's debug graphics display + +void Print_current_info(void); //Tony30Oct96 + + +#else // ie. not _DEBUG + +/* gcc doesn't like this - khalek +#define Zdebug NULL +#define Build_debug_text NULL +#define Draw_debug_graphics NULL +#define Print_current_info NULL +*/ + +void Zdebug(char * ,...); // Tony's special debug logging file March96 +void Build_debug_text(void); // James's debug text display +void Draw_debug_graphics(void); // James's debug graphics display + +#endif // _DEBUG // this whole file only included on debug versions +//-------------------------------------------------------------------------------------- + +void ExitWithReport(char *format,...); // (6dec96 JEL) IN BOTH DEBUG & RELEASE VERSIONS + + + +#endif //D_DEBUG diff --git a/sword2/defs.h b/sword2/defs.h new file mode 100644 index 0000000000..f90e662f07 --- /dev/null +++ b/sword2/defs.h @@ -0,0 +1,139 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef DEFS +#define DEFS + +#include "header.h" +#include "resman.h" + +//-------------------------------------------------------------------------------------- +#define SIZE 0x10000 //65536 items per section +#define NuSIZE 0xffff //& with this + +//-------------------------------------------------------------------------------------- +//global variable references + +// NB. 4 * <number from linc's Global Variables list> + +#define ID *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)) +#define RESULT *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1) // 4 * <number from linc's Global Variables list> +#define PLAYER_ACTION *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 2) +//#define CUR_PLAYER_ID *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 3) +#define CUR_PLAYER_ID 8 // always 8 (George object used for Nico player character as well) +#define PLAYER_ID *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 305) +#define TALK_FLAG *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 13) + +#define MOUSE_X *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 4) +#define MOUSE_Y *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 5) +#define LEFT_BUTTON *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 109) +#define RIGHT_BUTTON *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 110) +#define CLICKED_ID *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 178) + +#define IN_SUBJECT *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 6) +#define COMBINE_BASE *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 7) +#define OBJECT_HELD *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 14) + +#define SPEECH_ID *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 9) +#define INS1 *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 10) +#define INS2 *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 11) +#define INS3 *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 12) +#define INS4 *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 60) +#define INS5 *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 61) +#define INS_COMMAND *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 59) + +#define PLAYER_FEET_X *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 141) +#define PLAYER_FEET_Y *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 142) +#define PLAYER_CUR_DIR *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 937) + +#define LOCATION *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 62) // for debug.cpp + +#define SCROLL_X *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 345) // so scripts can force scroll offsets +#define SCROLL_Y *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 346) // so scripts can force scroll offsets + +#define EXIT_CLICK_ID *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 710) +#define EXIT_FADING *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 713) + +#define SYSTEM_TESTING_ANIMS *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 912) +#define SYSTEM_TESTING_TEXT *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1230) +#define SYSTEM_WANT_PREVIOUS_LINE *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1245) + +#define MOUSE_AVAILABLE *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 686) // 1=on 0=off (set in FN_add_human & FN_no_human) + +#define AUTO_SELECTED *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1115) // used in FN_choose + +#define CHOOSER_COUNT_FLAG *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 15) // see FN_start_conversation & FN_chooser + +#define DEMO *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1153) //signifies a demo mode + +#define PSXFLAG *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1173) // Indicates to script whether this is the Playstation version. + +#define DEAD *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1256) //=1 =dead +#define SPEECHANIMFLAG *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1278) // If set indicates that the speech anim is to run through only once. + +#define SCROLL_OFFSET_X *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 1314) //for the engine + +#define GAME_LANGUAGE *(uint32 *)(res_man.resList[1]->ad+sizeof(_standardHeader)+4* 111) //for the poor PSX so it knows what language is running. +//-------------------------------------------------------------------------------------- +//resource id's of pouse mointers. It's pretty much safe to do it like this + +#define NORMAL_MOUSE_ID 17 +#define SCROLL_LEFT_MOUSE_ID 1440 +#define SCROLL_RIGHT_MOUSE_ID 1441 + +//-------------------------------------------------------------------------------------- +// Console Font - does not use game text - only English required +#define CONSOLE_FONT_ID 340 // ConsFont + +// Speech Font +#define ENGLISH_SPEECH_FONT_ID 341 // SpchFont +#define FINNISH_SPEECH_FONT_ID 956 // FinSpcFn +#define POLISH_SPEECH_FONT_ID 955 // PolSpcFn + +// Control Panel Font (and un-selected savegame descriptions) +#define ENGLISH_CONTROLS_FONT_ID 2005 // Sfont +#define FINNISH_CONTROLS_FONT_ID 959 // FinSavFn +#define POLISH_CONTROLS_FONT_ID 3686 // PolSavFn + +// Red Font (for selected savegame descriptions) +#define ENGLISH_RED_FONT_ID 2005 // 1998 // Redfont +#define FINNISH_RED_FONT_ID 959 // 960 // FinRedFn +#define POLISH_RED_FONT_ID 3686 // 3688 // PolRedFn + +//-------------------------------------------------------------------------------------- +// Control panel palette resource id + +#define CONTROL_PANEL_PALETTE 261 + +//-------------------------------------------------------------------------------------- +// res id's of the system menu icons +#define OPTIONS_ICON 344 +#define QUIT_ICON 335 +#define SAVE_ICON 366 +#define RESTORE_ICON 364 +#define RESTART_ICON 342 + +//-------------------------------------------------------------------------------------- +// res id of conversation exit icon + +#define EXIT_ICON 65 // 'EXIT' menu icon (used in FN_choose) + +//-------------------------------------------------------------------------------------- + +#endif diff --git a/sword2/events.cpp b/sword2/events.cpp new file mode 100644 index 0000000000..b7f339c99a --- /dev/null +++ b/sword2/events.cpp @@ -0,0 +1,405 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +#include <stdio.h> + +//#include "src\driver96.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "events.h" +#include "interpreter.h" +#include "logic.h" +#include "memory.h" +#include "object.h" +#include "sync.h" +//------------------------------------------------------------------------------------ + +_event_unit event_list[MAX_events]; + +//------------------------------------------------------------------------------------ +void Init_event_system(void) //Tony4Dec96 +{ + + uint32 j; + + + for (j=0;j<MAX_events;j++) + event_list[j].id=0; //denotes free slot + +} +//------------------------------------------------------------------------------------ +uint32 CountEvents(void) +{ + uint32 j; + uint32 count=0; + + for (j=0; j<MAX_events; j++) + { + if (event_list[j].id) + count++; + } + + return (count); +} +//------------------------------------------------------------------------------------ +int32 FN_request_speech(int32 *params) //Tony13Nov96 +{ +//change current script - must be followed by a TERMINATE script directive + +//param 0 id of target to catch the event and startup speech servicing + + uint32 j=0; + + + + while(1) + { + if (event_list[j].id == (uint32)params[0]) + break; + if (!event_list[j].id) + break; + + j++; + } + + + + if (j==MAX_events) + Con_fatal_error("FN_set_event out of event slots (%s line %u)", __FILE__, __LINE__); + +//found that slot + + event_list[j].id=params[0]; //id of person to stop + event_list[j].interact_id=(params[0]*65536)+6; //full script id to interact with - megas run their own 7th script + + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +void Set_player_action_event(uint32 id, uint32 interact_id) //Tony4Dec96 +{ + uint32 j=0; + + + + +// if ((event_list[j].id!=id)&&(event_list[j].id)) +// while((event_list[j].id!=id)||(event_list[j].id)) //zip along until we find a free slot +// { j++; +// }; + + while(1) + { + if (event_list[j].id==id) + break; + if (!event_list[j].id) + break; + + j++; + } + + + if (j==MAX_events) + Con_fatal_error("Set_event out of event slots"); + +//found that slot + + event_list[j].id=id; //id of person to stop + event_list[j].interact_id=(interact_id*65536)+2; //full script id of action script number 2 + +} +//------------------------------------------------------------------------------------ +int32 FN_set_player_action_event(int32 *params) //Tony10Feb97 +{ +//we want to intercept the player character and have him interact with an object - from script +//this code is the same as the mouse engine calls when you click on an object - here, a third party does the clicking IYSWIM + +//note - this routine used CUR_PLAYER_ID as the target + +//params 0 id to interact with + + uint32 j=0; + + + + +//search for an existing event or a slot + + while(1) + { + if (event_list[j].id==CUR_PLAYER_ID) + break; + if (!event_list[j].id) + break; + + j++; + } + + + if (j==MAX_events) + Con_fatal_error("Set_event out of event slots"); + +//found that slot + + event_list[j].id=CUR_PLAYER_ID; //id of person to stop + event_list[j].interact_id=(params[0]*65536)+2; //full script id of action script number 2 + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_send_event(int32 *params) //Tony28Feb97 +{ +//we want to intercept the player character and have him interact with an object - from script + +// 0 id to recieve event +// 1 script to run + + + uint32 j=0; + + +// Zdebug("*+*+* %d %d", params[0], params[1] ); + + +//search for an existing event or a slot + + while(1) + { + if (event_list[j].id==(uint32)params[0]) + break; + if (!event_list[j].id) + break; + + j++; + } + + + if (j==MAX_events) + Con_fatal_error("fn_send_event out of event slots"); + +//found that slot + + event_list[j].id=params[0]; //id of person to stop + event_list[j].interact_id=params[1]; //full script id + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_check_event_waiting(int32 *params) //Tony4Dec96 +{ +// returns yes/no in RESULT + +// no params + + uint32 j; + + + + RESULT=0; + + + for (j=0; j<MAX_events; j++) + { + if (event_list[j].id == ID) //us? + { + RESULT=1; + break; + } + } + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// like FN_check_event_waiting, but starts the event rather than setting RESULT to 1 + +int32 FN_check_for_event(int32 *params) // James (04mar97) +{ + // no params + + uint32 j; + + + for (j=0; j<MAX_events; j++) + { + if (event_list[j].id == ID) //us? + { + // start the event + LLogic.Logic_one(event_list[j].interact_id); // run 3rd script of target object on level 1 + event_list[j].id = 0; // clear the event slot + return(IR_TERMINATE); + } + } + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// combination of FN_pause & FN_check_for_event +// - ie. does a pause, but also checks for event each cycle + +int32 FN_pause_for_event(int32 *params) // James (04mar97) +{ + // returns yes/no in RESULT + + // params: 0 pointer to object's logic structure + // 1 number of game-cycles to pause + + Object_logic *ob_logic = (Object_logic *)params[0]; + uint32 j; + + + // first, check for an event + + for (j=0; j<MAX_events; j++) + { + if (event_list[j].id == ID) // us? + { + ob_logic->looping = 0; // reset the 'looping' flag + // start the event + LLogic.Logic_one(event_list[j].interact_id); // run 3rd script of target object on level 1 + event_list[j].id = 0; // clear the event slot + return(IR_TERMINATE); + } + } + + + // no event, so do the FN_pause bit + + + if (ob_logic->looping==0) // start the pause + { + ob_logic->looping = 1; + ob_logic->pause = params[1]; // no. of game cycles + } + + if (ob_logic->pause) // if non-zero + { + ob_logic->pause--; // decrement the pause count + return(IR_REPEAT); // drop out of script, but call this again next cycle + } + else // pause count is zerp + { + ob_logic->looping = 0; + return(IR_CONT); // continue script + } +} + +//------------------------------------------------------------------------------------ +uint32 Check_event_waiting(void) //Tony4Dec96 +{ +//returns yes/no + + + uint32 j; + + + for (j=0;j<MAX_events;j++) + if (event_list[j].id == ID) //us? + return(1); //yes + + return(0); //no +} +//------------------------------------------------------------------------------------ +int32 FN_clear_event(int32 *params) //Tony11Mar97 +{ +// no params +// no return vaule + + + uint32 j; + + + for (j=0;j<MAX_events;j++) + if (event_list[j].id == ID) //us? + { + event_list[j].id=0; //clear the slot + return(IR_CONT); // + } + + return(IR_CONT); // +} +//------------------------------------------------------------------------------------ +void Start_event(void) //Tony4Dec96 +{ +//call this from stuff like fn_walk +//you must follow with a return(IR_TERMINATE) + + uint32 j; + + + for (j=0;j<MAX_events;j++) + if (event_list[j].id == ID) //us? + { + + LLogic.Logic_one( event_list[j].interact_id); //run 3rd script of target object on level 1 + + event_list[j].id=0; //clear the slot + + return; + } + +//oh dear - stop the system + + Con_fatal_error("Start_event can't find event for id %d", ID); + +} +//------------------------------------------------------------------------------------ +int32 FN_start_event(int32 *params) //Tony4Dec96 +{ + + uint32 j; + + + for (j=0;j<MAX_events;j++) + if (event_list[j].id == ID) //us? + { + + LLogic.Logic_one(event_list[j].interact_id); //run 3rd script of target object on level 1 + + event_list[j].id=0; //clear the slot + + return(IR_TERMINATE); + } + +//oh dear - stop the system + + Con_fatal_error("FN_start_event can't find event for id %d", ID); + + return(0); //never called - but lets stop them bloody errors +} +//------------------------------------------------------------------------------------ +void Kill_all_ids_events(uint32 id) //Tony18Dec96 +{ + uint32 j; + + + for (j=0;j<MAX_events;j++) + if (event_list[j].id == id) //us? + event_list[j].id=0; //clear the slot + + if (id); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + diff --git a/sword2/events.h b/sword2/events.h new file mode 100644 index 0000000000..d4fc6a632e --- /dev/null +++ b/sword2/events.h @@ -0,0 +1,48 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _EVENTS +#define _EVENTS + +//#include "src\driver96.h" +#include "object.h" + + +typedef struct +{ + uint32 id; + uint32 interact_id; +} _event_unit; + +#define MAX_events 10 + +extern _event_unit event_list[MAX_events]; + +void Init_event_system(void); //Tony4Dec96 +int32 FN_set_event(int32 *params); //Tony13Nov96 +void Set_player_action_event(uint32 id, uint32 interact_id); //Tony4Dec96 +int32 FN_check_event_waiting(void); //Tony4Dec96 +void Start_event(void); //Tony4Dec96 +int32 FN_start_event(void); //Tony4Dec96 +uint32 Check_event_waiting(void); //Tony4Dec96 +void Kill_all_ids_events(uint32 id); //Tony18Dec96 +uint32 CountEvents(void); // James11july97 + + +#endif diff --git a/sword2/function.cpp b/sword2/function.cpp new file mode 100644 index 0000000000..214924a503 --- /dev/null +++ b/sword2/function.cpp @@ -0,0 +1,458 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#include "driver/driver96.h" +#include "build_display.h" +#include "credits.h" +#include "debug.h" +#include "defs.h" +#include "function.h" +#include "interpreter.h" +#include "layers.h" // for 'this_screen' structure +#include "logic.h" +#include "memory.h" +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "sword2.h" // for CloseGame() +//------------------------------------------------------------------------------------ +typedef struct +{ + uint32 a; + uint32 b; +} test_struct; + +//------------------------------------------------------------------------------------ + +Object_graphic engine_graph; // global for engine +Object_mega engine_mega; // global for engine + +//------------------------------------------------------------------------------------ +int32 FN_test_function(int32 *params) +{ +//param 0 address of a flag + Zdebug(" TEST %d %d", *params, RESULT); + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_test_flags(int32 *params) +{ +//param 0 value of flag + + test_struct *tony; + + + tony = (test_struct*) *params; //address of structure + + + +// Zdebug("\nFN_test_flags %d, %d\n", tony->a, tony->b ); + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_gosub(int32 *params) //Tony23Sept96 +{ +//hurray, script subroutines +//param 0 id of script + + + LLogic.Logic_up(*params); + + return(4); //logic goes up - pc is saved for current level +} +//------------------------------------------------------------------------------------ +int32 FN_new_script(int32 *params) //Tony13Nov96 +{ +//change current script - must be followed by a TERMINATE script directive +//param 0 id of script + Zdebug("FN_new_script %d", *params); + + PLAYER_ACTION=0; //must clear this + + LLogic.Logic_replace( *params ); + + return(IR_TERMINATE); //drop out no pc save - and around again +} +//------------------------------------------------------------------------------------ +int32 FN_interact(int32 *params) //Tony13Nov96 +{ +//run targets action on a subroutine +//called by player on his base level 0 idle, for example + + +//param 0 id of target from which we derive action script reference + + Zdebug("FN_interact %d", *params); + PLAYER_ACTION=0; //must clear this + + LLogic.Logic_up( (*params*65536)+2); //3rd script of clicked on id + + return(IR_GOSUB); //out, up and around again - pc is saved for current level to be returned to +} +//------------------------------------------------------------------------------------ + +// Open & close a resource. +// Forces a resource into memory before it's "officially" opened for use. +// eg. if an anim needs to run on smoothly from another, "preloading" gets it into memory in advance +// to avoid the cacheing delay that normally occurs before the first frame. + +int32 FN_preload(int32 *params) // (1Nov96 JEL) +{ + res_man.Res_open(params[0]); // open resource + res_man.Res_close(params[0]); // close resource + + return(IR_CONT); // continue script +} + + +// Go fetch resource in the background. +int32 FN_prefetch(int32 *params) +{ + return(IR_CONT); +} + + +// Fetches a resource in the background but prevents the script from continuing until the resource is in memory. +int32 FN_fetch_wait(int32 *params) +{ + return (IR_CONT); +} + + +// Releases a resource from memory. Used for freeing memory for sprites that have just been used +// and will not be used again. +// Sometimes it is better to kick out a sprite straight away so that the memory can be used for +// more frequent animations. +int32 FN_release(int32 *params) +{ + return (IR_CONT); +} + + +//------------------------------------------------------------------------------------ +// Generates a random number between 'min' & 'max' inclusive, and sticks it in the script flag 'result' + +int32 FN_random(int32 *params) // (1nov96 JEL) +{ + uint32 min = params[0]; + uint32 max = params[1]; + + RESULT = (rand() % (max-min+1)) + min; // return_value = random integer between min and max, inclusive + + return(IR_CONT); // continue script +} +//------------------------------------------------------------------------------------ +int32 FN_pause(int32 *params) // (19nov96 JEL) +{ + // params: 0 pointer to object's logic structure + // 1 number of game-cycles to pause + + //NB. Pause-value of 0 causes script to continue, 1 causes a 1-cycle quit, 2 gives 2 cycles, etc. + + Object_logic *ob_logic = (Object_logic *)params[0]; + + if (ob_logic->looping==0) // start the pause + { + ob_logic->looping = 1; + ob_logic->pause = params[1]; // no. of game cycles + } + + if (ob_logic->pause) // if non-zero + { + ob_logic->pause--; // decrement the pause count + return(IR_REPEAT); // drop out of script, but call this again next cycle + } + else // pause count is zerp + { + ob_logic->looping = 0; + return(IR_CONT); // continue script + } +} +//------------------------------------------------------------------------------------ +int32 FN_random_pause(int32 *params) // (26nov96 JEL) +{ + // params: 0 pointer to object's logic structure + // 1 minimum number of game-cycles to pause + // 2 maximum number of game-cycles to pause + + Object_logic *ob_logic = (Object_logic *)params[0]; + int32 pars[2]; + + + if (ob_logic->looping==0) + { + pars[0] = params[1]; // min + pars[1] = params[2]; // max + + FN_random(pars); + + pars[1] = RESULT; // random value between 'min' & 'max' inclusive + } + + pars[0] = params[0]; // &logic + + return FN_pause(pars); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +int32 FN_pass_graph(int32 *params) //Tony28Nov96 +{ +//makes an engine local copy of passed graphic_structure and mega_structure - run script 4 of an object to request this +//used by FN_turn_to(id) etc +//remember, we cannot simply read a compact any longer but instead must request it from the object itself + +//params 0 pointer to a graphic structure *might not need this? + + + memcpy( &engine_graph, (uint8*)params[0], sizeof(Object_graphic)); + + return(IR_CONT); //makes no odds +} +//------------------------------------------------------------------------------------ +int32 FN_pass_mega(int32 *params) //Tony28Nov96 +{ +//makes an engine local copy of passed graphic_structure and mega_structure - run script 4 of an object to request this +//used by FN_turn_to(id) etc +//remember, we cannot simply read a compact any longer but instead must request it from the object itself + +//params 0 pointer to a mega structure + + + memcpy( &engine_mega, (uint8*)params[0], sizeof(Object_mega)); + + return(IR_CONT); //makes no odds +} +//------------------------------------------------------------------------------------ +// temp. function! +// used for setting far-referenced megaset resource field in mega object, from start script + +int32 FN_set_value(int32 *params) // (02jan97 JEL) +{ + // params: 0 pointer to object's mega structure + // 1 value to set it to + + Object_mega *ob_mega = (Object_mega *)params[0]; + + + ob_mega->megaset_res = params[1]; + + return(IR_CONT); // continue script +} +//------------------------------------------------------------------------------------ +#define BLACK 0 +#define WHITE 1 +#define RED 2 +#define GREEN 3 +#define BLUE 4 +//------------------------------------------------------------------------------------ +uint8 black[4] = {0,0,0,0}; +uint8 white[4] = {255,255,255,0}; +uint8 red[4] = {255,0,0,0}; +uint8 green[4] = {0,255,0,0}; +uint8 blue[4] = {0,0,255,0}; +//------------------------------------------------------------------------------------ +// flash colour 0 (ie. border) - useful during script development +// eg. FN_flash(BLUE) where a text line is missed; RED when some code missing, etc + +int32 FN_flash(int32 *params) // (James14feb97) +{ + // params 0: colour to flash + +#ifdef _DEBUG + + uint32 count; + + switch (params[0]) // what colour? + { + case WHITE: + SetPalette(0, 1, white, RDPAL_INSTANT); + break; + + case RED: + SetPalette(0, 1, red, RDPAL_INSTANT); + break; + + case GREEN: + SetPalette(0, 1, green, RDPAL_INSTANT); + break; + + case BLUE: + SetPalette(0, 1, blue, RDPAL_INSTANT); + break; + } + + for (count=0; count<0x80000; count++) + { + count++; + count--; + } + + SetPalette(0, 1, black, RDPAL_INSTANT); + +#endif // _DEBUG + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// set border colour - useful during script development +// eg. set to colour during a timer situation, then black when timed out + +int32 FN_colour(int32 *params) // (James14feb97) +{ + // params 0: colour (see defines above) + +#ifdef _DEBUG + + switch (params[0]) // what colour? + { + case BLACK: + SetPalette(0, 1, black, RDPAL_INSTANT); + break; + + case WHITE: + SetPalette(0, 1, white, RDPAL_INSTANT); + break; + + case RED: + SetPalette(0, 1, red, RDPAL_INSTANT); + break; + + case GREEN: + SetPalette(0, 1, green, RDPAL_INSTANT); + break; + + case BLUE: + SetPalette(0, 1, blue, RDPAL_INSTANT); + break; + } + +#endif // _DEBUG + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// Display a message to the user on the screen. +// + +int32 FN_display_msg(int32 *params) // (Chris 15/5/97) +{ + // params 0: Text number of message to be displayed. + uint32 local_text = params[0]&0xffff; + uint32 text_res = params[0]/SIZE; + + // Display message for three seconds. + DisplayMsg(FetchTextLine( res_man.Res_open(text_res), local_text )+2, 3); // +2 to skip the encoded text number in the first 2 chars; 3 is duration in seconds + res_man.Res_close(text_res); + RemoveMsg(); + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// FN_reset_globals is used by the demo - so it can loop back & restart itself +int32 FN_reset_globals(int32 *params) //Tony29May97 +{ + int32 size; + uint32 *globals; + int j; + + size = res_man.Res_fetch_len(1); + + size-=sizeof(_standardHeader); + + Zdebug("\nglobals size %d", size/4); + + globals = (uint32*) ((uint8 *) res_man.Res_open(1)+sizeof(_standardHeader)); + + for (j=0;j<size/4;j++) + globals[j]=0; //blank each global variable + + res_man.Res_close(1); + + res_man.Kill_all_objects(0); //all objects but george + +// SetGlobalInterpreterVariables((int32*)(res_man.Res_open(1)+sizeof(_standardHeader))); //reopen global variables resource & send address to interpreter - it won't be moving +// res_man.Res_close(1); + + //--------------------------------------------------------------- + // FOR THE DEMO - FORCE THE SCROLLING TO BE RESET! (James29may97) + // - this is taken from FN_init_background + this_screen.scroll_flag = 2; // switch on scrolling (2 means first time on screen) + //--------------------------------------------------------------- + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +// FN_play_credits - Plays the credits? +// This function just quits the game if this is the playable demo, ie. credits are NOT played in the demo any more! + +extern uint8 quitGame; // From sword2.cpp +extern void UpdateCompSampleStreaming(void); // From d_sound.c + +int32 FN_play_credits(int32 *params) +{ + +/* uint32 rv; // for Credits() return value + + if (!DEMO) // this ju + { + _drvDrawStatus ds; + _drvSoundStatus ss; + _drvKeyStatus ks; + + ClearAllFx(); // Must stop all fx + CloseFx(-2); // including leadins + CloseFx(-1); // including leadouts + StopMusic(); // Stop any streaming music + + for (int i = 0; i<16; i++) + UpdateCompSampleStreaming(); // And wait for it to die + + GetDrawStatus (&ds); + GetSoundStatus(&ss); + GetKeyStatus (&ks); + + rv = Credits(&ds, &ss, res_man.GetCdPath(), GetRenderType()==3, &gotTheFocus, &ks); + SetDrawStatus (&ds); // (James14aug97) Because game crashing when trying to close down after credits + SetSoundStatus(&ss); // -"- + } + + // returns non-zero if Ctrl-Q was pressed to quit the game during the credits + + if (rv || DEMO) // if Ctrl-Q pressed during credits, or if this is the playable demo + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); // quit the game + } + +*/ + return (IR_CONT); +} +//------------------------------------------------------------------------------------ + diff --git a/sword2/function.h b/sword2/function.h new file mode 100644 index 0000000000..963bd6acff --- /dev/null +++ b/sword2/function.h @@ -0,0 +1,31 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _FUNCTION +#define _FUNCTION + +//#include "src\driver96.h" +#include "object.h" + + +extern Object_graphic engine_graph; // global for engine +extern Object_mega engine_mega; // global for engine + + +#endif diff --git a/sword2/header.h b/sword2/header.h new file mode 100644 index 0000000000..b4caaab3c4 --- /dev/null +++ b/sword2/header.h @@ -0,0 +1,355 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _HEADER +#define _HEADER + +#include "common/scummsys.h" +//#include "src\driver96.h" + +//---------------------------------------------------------- +// SYSTEM FILE & FRAME HEADERS (23sep96 JEL) +//---------------------------------------------------------- + +//#pragma pack( push ) +//#pragma pack( 1 ) + +#if !defined(__GNUC__) + #pragma START_PACK_STRUCTS +#endif + + + +//---------------------------------------------------------- +// ALL FILES +//---------------------------------------------------------- +// Standard File Header +#define NAME_LEN 34 + +typedef struct +{ + uint8 fileType; // byte to define file type (see below) + uint8 compType; // type of file compression used ie. on whole file (see below) + uint32 compSize; // length of compressed file (ie. length on disk) + uint32 decompSize; // length of decompressed file held in memory (NB. frames still held compressed) + uint8 name[NAME_LEN]; //name of object +} GCC_PACK _standardHeader; + +//---------------------------------------------------------- +// fileType + +// 0 something's wrong! +#define ANIMATION_FILE 1 // all normal animations & sprites including mega-sets & font files which are the same format (but all frames always uncompressed) +#define SCREEN_FILE 2 // each contains background, palette, layer sprites, parallax layers & shading mask +#define GAME_OBJECT 3 // each contains object hub + structures + script data +#define WALK_GRID_FILE 4 // walk-grid data +#define GLOBAL_VAR_FILE 5 // all the global script variables in one file; "there can be only one" +#define PARALLAX_FILE_null 6 // NOT USED +#define RUN_LIST 7 // each contains a list of object resource id's +#define TEXT_FILE 8 // each contains all the lines of text for a location or a character's conversation script +#define SCREEN_MANAGER 9 // one for each location; this contains special startup scripts +#define MOUSE_FILE 10 // mouse pointers and luggage icons (sprites in General \ Mouse pointers & Luggage icons) +#define WAV_FILE 11 // wav file +#define ICON_FILE 12 // menu icon (sprites in General \ Menu icons +#define PALETTE_FILE 13 // separate palette file (see also _paletteHeader) +//---------------------------------------------------------- +// compType + +#define NO_COMPRESSION 0 +#define FILE_COMPRESSION 1 // standard whole-file compression (not yet devised!) + +//---------------------------------------------------------- + + + +//---------------------------------------------------------- +// (1) ANIMATION FILES +//---------------------------------------------------------- +// an animation file consists of: + +// standard file header +// animation header +// a string of CDT entries (one per frame of the anim) +// a 16-byte colour table ONLY if (runTimeComp==RLE16) +// a string of groups of (frame header + frame data) + +//---------------------------------------------------------- +// Animation Header + +typedef struct +{ + uint8 runTimeComp; // type of runtime compression used for the frame data (see below) + uint16 noAnimFrames; // number of frames in the anim (ie. no. of CDT entries) + uint16 feetStartX; // start coords for mega to walk to, before running anim + uint16 feetStartY; + uint8 feetStartDir; // direction to start in before running anim + uint16 feetEndX; // end coords for mega to stand at after running anim (vital if anim starts from an off-screen position, or ends in a different place from the start) + uint16 feetEndY; + uint8 feetEndDir; // direction to start in after running anim + uint16 blend; +} GCC_PACK _animHeader; + +//---------------------------------------------------------- +// runtimeComp - compression used on each frame of the anim + +#define NONE 0 // No frame compression +#define RLE256 1 // James's RLE for 256-colour sprites +#define RLE16 2 // James's RLE for 16- or 17-colour sprites + // (raw blocks have max 16 colours for 2 pixels per byte, + // so '0's are encoded only as FLAT for 17-colour sprites eg. George's mega-set) +//---------------------------------------------------------- +// CDT Entry + +typedef struct +{ + int16 x; // sprite x-coord OR offset to add to mega's feet x-coord to calc sprite y-coord + int16 y; // sprite y-coord OR offset to add to mega's feet y-coord to calc sprite y-coord + uint32 frameOffset; // points to start of frame header (from start of file header) + uint8 frameType; // 0=print sprite normally with top-left corner at (x,y), otherwise see below... +} GCC_PACK _cdtEntry; + +// 'frameType' bit values +#define FRAME_OFFSET 1 // print at (feetX+x,feetY+y), with scaling according to feetY +#define FRAME_FLIPPED 2 // print the frame flipped Left->Right +#define FRAME_256_FAST 4 // Frame has been compressed using Pauls fast RLE 256 compression. + +//---------------------------------------------------------- +//Frame Header + +typedef struct +{ + uint32 compSize; // compressed size of frame - NB. compression type is now in Anim Header + uint16 width; // dimensions of frame + uint16 height; +} GCC_PACK _frameHeader; + +//---------------------------------------------------------- +// Frame Data + +// uint8 spriteData[width*height]; // one byte per pixel +//---------------------------------------------------------- + + + + + +//---------------------------------------------------------- +// (2) SCREEN FILES +//---------------------------------------------------------- +// a screen file consists of: + +// standard file header +// multi screen header +// 4*256 bytes of palette data +// 256k palette match table +// 2 background parallax layers +// 1 background layer with screen header +// 2 foreground parallax layers +// a string of layer headers +// a string of layer masks + +//---------------------------------------------------------- +// Multi screen header +// Goes at the beginning of a screen file after the standard +// header. +// Gives offsets from start of table of each of the components + +typedef struct +{ + uint32 palette; + uint32 bg_parallax[2]; + uint32 screen; + uint32 fg_parallax[2]; + uint32 layers; + uint32 paletteTable; + uint32 maskOffset; +} GCC_PACK _multiScreenHeader; + +//------------------------------------------------------------ +// Palette Data + +typedef struct +{ + uint8 red; + uint8 green; + uint8 blue; + uint8 alpha; +} GCC_PACK _palEntry; + +#define NO_COLOURS 256 +// _palEntry palette[NO_COLOURS] + +//------------------------------------------------------------ +// Screen Header + +typedef struct +{ + uint16 width; // dimensions of the background screen + uint16 height; + uint16 noLayers; // number of layer areas +} GCC_PACK _screenHeader; + +//------------------------------------------------------------ +// Background Raw Bitmap + +// uint8 backgroundData[width*height]; // one byte per pixel + +//------------------------------------------------------------ +// Layer Header + +// Note that all the layer headers are kept together, +// rather than being placed before each layer mask, +// in order to simplify the sort routine. + +typedef struct +{ + uint16 x; // coordinates of top-left pixel of area + uint16 y; + uint16 width; + uint16 height; + uint32 maskSize; + uint32 offset; // where to find mask data (from start of standard file header) +} GCC_PACK _layerHeader; + +//------------------------------------------------------------ +// Layer Mask + +// uint8 layerData[width*height/8]; // 8 pixels to a byte + +//------------------------------------------------------------ + + + +//---------------------------------------------------------- +// (3) SCRIPT OBJECT FILES +//---------------------------------------------------------- +// a script object file consists of: + +// standard file header +// script object header +// script object data + +//---------------------------------------------------------- +// Script Object Header + +// ??????? +// ??????? +// ??????? + +//---------------------------------------------------------- +// Script Object Data + +//---------------------------------------------------------- + + + +//---------------------------------------------------------- +// (4) WALK-GRID FILES +//---------------------------------------------------------- +// a walk-grid file consists of: + +// standard file header +// walk-grid file header +// walk-grid data + +//---------------------------------------------------------- +// Walk-Grid Header - taken directly from old "header.h" in STD_INC + +typedef struct +{ + int32 numBars; // number of bars on the floor + int32 numNodes; // number of nodes +} GCC_PACK _walkGridHeader; + +//---------------------------------------------------------- +// Walk-Grid Data + +// ??????? + +//---------------------------------------------------------- + + + +//---------------------------------------------------------- +// (5) PALETTE FILES +//---------------------------------------------------------- +// a palette file consists of: + +// standard file header +// 4*256 bytes of palette data +// 256k palette match table + +//---------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- +// PCX file header +//-------------------------------------------------------------------------------------------------- +typedef struct +{ uint8 manufacturer; + uint8 version; + uint8 encoding; + uint8 bitsPerPixel; + int16 xmin,ymin; + int16 xmax,ymax; + int16 hres; + int16 vres; + char palette[48]; + char reserved; + uint8 colourPlanes; + int16 bytesPerLine; + int16 paletteType; + char filler[58]; +} GCC_PACK _PCXHEAD; +//---------------------------------------------------------- +#define TREE_SIZE 3 + +typedef struct // an object hub - which represents all that remains of the compact concept +{ + int32 type; // type of object + uint32 logic_level; // what level? + uint32 logic[TREE_SIZE]; // NOT USED + uint32 script_id[TREE_SIZE]; // need this if script + uint32 script_pc[TREE_SIZE]; // need this also +} GCC_PACK _object_hub; +//---------------------------------------------------------- +//---------------------------------------------------------- +// (6) text module header TW + +typedef struct +{ + uint32 noOfLines; //how many lines of text are there in this module +} GCC_PACK _textHeader; + +//a text file has: + +// _standardHeader +// _textHeader +// look up table, to +// line of text,0 +// line of text,0 + +//---------------------------------------------------------- + +// #pragma pack( pop ) + +#if !defined(__GNUC__) + #pragma END_PACK_STRUCTS +#endif + +#endif diff --git a/sword2/icons.cpp b/sword2/icons.cpp new file mode 100644 index 0000000000..880fcbc0da --- /dev/null +++ b/sword2/icons.cpp @@ -0,0 +1,244 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "driver/driver96.h" +#include "console.h" +#include "icons.h" +#include "interpreter.h" +#include "logic.h" +#include "mouse.h" +#include "object.h" + +//------------------------------------------------------------------------------------ +menu_object temp_list[TOTAL_engine_pockets]; +uint32 total_temp=0; //tempory list + +menu_object master_menu_list[TOTAL_engine_pockets]; +uint32 total_masters=0; +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +int32 FN_add_menu_object(int32 *params) //Tony1Oct96 +{ +//param 0 pointer to a menu_object structure to copy down + +// Zdebug("FN_add_menu_object icon res"); + +#ifdef _DEBUG + if (total_temp == TOTAL_engine_pockets) + Con_fatal_error("TOTAL_engine_pockets exceeded! (%s line %u)",__FILE__,__LINE__); +#endif + +// copy the structure to our in-the-engine list + memcpy( &temp_list[total_temp], (uint8*) *params, sizeof(menu_object)); // + total_temp++; + + return(IR_CONT); // script continue +} +//------------------------------------------------------------------------------------ +int32 FN_refresh_inventory(int32 *params) // (James28aug97) +{ + // called from 'menu_look_or_combine' script in 'menu_master' object + // to update the menu to display a combined object while George runs voice-over + // Note that 'object_held' must be set to the graphic of the combined object + + COMBINE_BASE=0; // can reset this now + + examining_menu_icon=1; // so that the icon in 'object_held' is coloured while the rest are grey + Build_top_menu(); + examining_menu_icon=0; + + return(IR_CONT); // script continue +} +//------------------------------------------------------------------------------------ +void Build_top_menu(void) //Tony19Nov96 +{ + // create and start the inventory menu - NOW AT THE BOTTOM OF THE SCREEN! + + uint32 null_pc=0; + uint32 j,k; + uint8 icon_coloured; + uint8 *icon; + uint8 *head; + uint32 res; + + total_temp=0; //reset temp list which will be totally rebuilt + + + +// Zdebug("\nbuild top menu %d", total_masters); + + +//clear the temp list before building a new temp list in-case list gets smaller + for (j=0;j<TOTAL_engine_pockets;j++) //check each master + temp_list[j].icon_resource=0; // + + +//call menu builder script which will register all carried menu objects + head = res_man.Res_open(MENU_MASTER_OBJECT); + RunScript( (char*)head, (char*)head, &null_pc ); // run the 'build_menu' script in the 'menu_master' object + res_man.Res_close(MENU_MASTER_OBJECT); + +//compare new with old +//anything in master thats not in new gets removed from master - if found in new too, remove from temp + + if (total_masters) + { + for (j=0;j<total_masters;j++) //check each master + { + for (k=0;k<TOTAL_engine_pockets;k++) + { + res=0; + if (master_menu_list[j].icon_resource == temp_list[k].icon_resource) //if master is in temp + { + temp_list[k].icon_resource=0; //kill it in the temp + res=1; + break; + } + } + if (!res) + { master_menu_list[j].icon_resource=0; //otherwise not in temp so kill in main +// Zdebug("Killed menu %d",j); + } + } + } + +//merge master downwards + + total_masters=0; + for (j=0;j<TOTAL_engine_pockets;j++) //check each master slot + { + if ((master_menu_list[j].icon_resource)&&(j!=total_masters)) //not current end - meaning out over the end so move down + { + memcpy( &master_menu_list[total_masters++], &master_menu_list[j], sizeof(menu_object)); // + master_menu_list[j].icon_resource=0; //moved down now so kill here + } + else if (master_menu_list[j].icon_resource) //skip full slots + total_masters++; + } + +//add those new to menu still in temp but not yet in master to the end of the master + for (j=0;j<TOTAL_engine_pockets;j++) //check each master slot + if (temp_list[j].icon_resource) //here's a new temp + memcpy( &master_menu_list[total_masters++], &temp_list[j], sizeof(menu_object)); // + + +//init top menu from master list + for (j=0;j<15;j++) + { + if (master_menu_list[j].icon_resource) + { + res = master_menu_list[j].icon_resource; // 'res' is now the resource id of the icon + + //----------------------------------------------------------------------------------------------------- + // WHEN AN ICON HAS BEEN RIGHT-CLICKED FOR 'EXAMINE' - SELECTION COLOURED, THE REST GREYED OUT + + if (examining_menu_icon) // '1' when examining a menu-icon ('OBJECT_HELD' is the resource of the icon being examined) + { + if (res == OBJECT_HELD) // if this is the icon being examined, make it coloured + icon_coloured=1; + else // if not, grey this one out + icon_coloured=0; + } + //----------------------------------------------------------------------------------------------------- + // WHEN ONE MENU OBJECT IS BEING USED WITH ANOTHER - BOTH TO BE COLOURED, THE REST GREYED OUT + + else if (COMBINE_BASE) // resource of second icon clicked + { + if ((res == OBJECT_HELD)||(res == COMBINE_BASE)) // if this if either of the icons being combined... + icon_coloured=1; + else + icon_coloured=0; + } + //----------------------------------------------------------------------------------------------------- + // NORMAL ICON SELECTION - SELECTION GREYED OUT, THE REST COLOURED + + else + { + if (res == OBJECT_HELD) // if this is the selction, grey it out + icon_coloured=0; + else // if not, make it coloured + icon_coloured=1; + } + + //----------------------------------------------------------------------------------------------------- + + + if (icon_coloured) // coloured + icon = res_man.Res_open( master_menu_list[j].icon_resource ) + sizeof(_standardHeader) + RDMENU_ICONWIDE*RDMENU_ICONDEEP; + else // greyed out + icon = res_man.Res_open( master_menu_list[j].icon_resource ) + sizeof(_standardHeader); + + SetMenuIcon(RDMENU_BOTTOM, j, icon); + res_man.Res_close( res ); + } + else + { + SetMenuIcon(RDMENU_BOTTOM, j, NULL); //no icon here + //Zdebug(" NULL for %d", j); + } + } + + ShowMenu(RDMENU_BOTTOM); + +} + + + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void Build_system_menu(void) //Tony19Mar97 +{ +//start a fresh top system menu + + uint8 *icon; + int j; + + uint32 icon_list[5] = + { + OPTIONS_ICON, + QUIT_ICON, + SAVE_ICON, + RESTORE_ICON, + RESTART_ICON + }; + + + for (j=0;j<5;j++) //build them all high in full colour - when one is clicked on all the rest will grey out + { + if ((DEAD)&&(j==2)) //dead then SAVE not available + icon = res_man.Res_open( icon_list[j] ) + sizeof(_standardHeader); + + else icon = res_man.Res_open( icon_list[j] ) + sizeof(_standardHeader) + RDMENU_ICONWIDE*RDMENU_ICONDEEP; + SetMenuIcon(RDMENU_TOP, j, icon); + res_man.Res_close( icon_list[j] ); + } + + + ShowMenu(RDMENU_TOP); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ diff --git a/sword2/icons.h b/sword2/icons.h new file mode 100644 index 0000000000..c841a01633 --- /dev/null +++ b/sword2/icons.h @@ -0,0 +1,42 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _ICONS +#define _ICONS + + +//#include "src\driver96.h" +#include "object.h" + +#define MENU_MASTER_OBJECT 44 +#define TOTAL_subjects 375-256+1 //the speech subject bar +#define TOTAL_engine_pockets 15+10 // +10 for overflow + +typedef struct +{ + int32 icon_resource; // icon graphic graphic + int32 luggage_resource; // luggage icon resource (for attaching to mouse pointer) +} menu_object; //define these in a script and then register them with the system + +extern menu_object master_menu_list[TOTAL_engine_pockets]; + +void Build_top_menu(void); //Tony19Nov96 +void Build_system_menu(void); //Tony19Mar97 + +#endif diff --git a/sword2/interpreter.cpp b/sword2/interpreter.cpp new file mode 100644 index 0000000000..91bf84f3ad --- /dev/null +++ b/sword2/interpreter.cpp @@ -0,0 +1,791 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef INSIDE_LINC // Are we running in linc? +#include "console.h" +#endif + +#include "driver/driver96.h" +#include "interpreter.h" + + + +// This file serves two purposes. It is compiled as part of the test functions +// of Linc, and also as part of the game + + +// The machine code table + +int32 FN_test_function(int32 *params); +int32 FN_test_flags(int32 *params); +int32 FN_register_start_point(int32 *params); +int32 FN_init_background(int32 *params); +int32 FN_set_session(int32 *params); +int32 FN_back_sprite(int32 *params); +int32 FN_sort_sprite(int32 *params); +int32 FN_fore_sprite(int32 *params); +int32 FN_register_mouse(int32 *params); +int32 FN_anim(int32 *); +int32 FN_random(int32 *); +int32 FN_preload(int32 *); +int32 FN_add_subject(int32 *); +int32 FN_interact(int32 *); +int32 FN_choose(int32 *); +int32 FN_walk(int32 *); +int32 FN_walk_to_anim(int32 *); // walk to start position of anim +int32 FN_turn(int32 *); // turn to (dir) +int32 FN_stand_at(int32 *); // stand at (x,y,dir) +int32 FN_stand(int32 *); // stand facing (dir) +int32 FN_stand_after_anim(int32 *); // stand at end position of anim +int32 FN_pause(int32 *); +int32 FN_mega_table_anim(int32 *); +int32 FN_add_menu_object(int32 *); +int32 FN_start_conversation(int32 *); +int32 FN_end_conversation(int32 *); +int32 FN_set_frame(int32 *); +int32 FN_random_pause(int32 *); +int32 FN_register_frame(int32 *); +int32 FN_no_sprite(int32 *); +int32 FN_send_sync(int32 *); +int32 FN_update_player_stats(int32 *); +int32 FN_pass_graph(int32 *); +int32 FN_init_floor_mouse(int32 *); +int32 FN_pass_mega(int32 *); +int32 FN_face_xy(int32 *); +int32 FN_end_session(int32 *); +int32 FN_no_human(int32 *); +int32 FN_add_human(int32 *); +int32 FN_we_wait(int32 *); +int32 FN_they_do_we_wait(int32 *); +int32 FN_they_do(int32 *); +int32 FN_walk_to_talk_to_mega(int32 *); +int32 FN_fade_down(int32 *); +int32 FN_i_speak(int32 *); +int32 FN_total_restart(int32 *); +int32 FN_set_walkgrid(int32 *); +int32 FN_speech_process(int32 *); +int32 FN_set_scaling(int32 *); +int32 FN_start_event(int32 *); +int32 FN_check_event_waiting(int32 *); +int32 FN_request_speech(int32 *); +int32 FN_gosub(int32 *); +int32 FN_timed_wait(int32 *); +int32 FN_play_fx(int32 *); +int32 FN_stop_fx(int32 *); +int32 FN_play_music(int32 *); +int32 FN_stop_music(int32 *); +int32 FN_set_value(int32 *); +int32 FN_new_script(int32 *); +int32 FN_get_sync(int32 *); +int32 FN_wait_sync(int32 *); +int32 FN_register_walkgrid(int32 *); +int32 FN_reverse_mega_table_anim(int32 *); +int32 FN_reverse_anim(int32 *); +int32 FN_add_to_kill_list(int32 *); +int32 FN_set_standby_coords(int32 *); +int32 FN_back_par0_sprite(int32 *params); +int32 FN_back_par1_sprite(int32 *params); +int32 FN_fore_par0_sprite(int32 *params); +int32 FN_fore_par1_sprite(int32 *params); +int32 FN_fore_par1_sprite(int32 *params); +int32 FN_set_player_action_event(int32 *params); +int32 FN_set_scroll_coordinate(int32 *params); +int32 FN_stand_at_anim(int32 *params); +int32 FN_set_scroll_left_mouse(int32 *params); +int32 FN_set_scroll_right_mouse(int32 *params); +int32 FN_colour(int32 *params); +int32 FN_flash(int32 *params); +int32 FN_prefetch(int32 *params); +int32 FN_get_player_savedata(int32 *params); +int32 FN_pass_player_savedata(int32 *params); +int32 FN_send_event(int32 *params); +int32 FN_add_walkgrid(int32 *params); +int32 FN_remove_walkgrid(int32 *params); +int32 FN_check_for_event(int32 *params); +int32 FN_pause_for_event(int32 *params); +int32 FN_clear_event(int32 *params); +int32 FN_face_mega(int32 *params); +int32 FN_play_sequence(int32 *params); +int32 FN_shaded_sprite(int32 *params); +int32 FN_unshaded_sprite(int32 *params); +int32 FN_fade_up(int32 *params); +int32 FN_display_msg(int32 *params); +int32 FN_set_object_held(int32 *params); +int32 FN_add_sequence_text(int32 *params); +int32 FN_reset_globals(int32 *params); +int32 FN_set_palette(int32 *params); +int32 FN_register_pointer_text(int32 *params); +int32 FN_fetch_wait(int32 *params); +int32 FN_release(int32 *params); +int32 FN_sound_fetch(int32 *params); +int32 FN_prepare_music(int32 *params); +int32 FN_smacker_lead_in(int32 *params); +int32 FN_smacker_lead_out(int32 *params); +int32 FN_stop_all_fx(int32 *params); +int32 FN_check_player_activity(int32 *params); +int32 FN_reset_player_activity_delay(int32 *params); +int32 FN_check_music_playing(int32 *params); +int32 FN_play_credits(int32 *params); +int32 FN_set_scroll_speed_normal(int32 *params); +int32 FN_set_scroll_speed_slow(int32 *params); +int32 FN_remove_chooser(int32 *params); +int32 FN_set_fx_vol_and_pan(int32 *params); +int32 FN_set_fx_vol(int32 *params); +int32 FN_restore_game(int32 *params); +int32 FN_refresh_inventory(int32 *params); +int32 FN_change_shadows(int32 *params); + +#define MAX_FN_NUMBER 117 + +extern int32 (*McodeTable[])(int32 *); + +#ifndef INSIDE_LINC +int32 * globalInterpreterVariables2 = NULL; // Point to the global varibale data +int g_debugFlag = 0; // Set this to turn debugging on +#endif + + +int32 (*McodeTable[MAX_FN_NUMBER+1])(int32 *) = +{ FN_test_function, + FN_test_flags, + FN_register_start_point, + FN_init_background, + FN_set_session, + FN_back_sprite, + FN_sort_sprite, + FN_fore_sprite, + FN_register_mouse, + FN_anim, + FN_random, + FN_preload, + FN_add_subject, + FN_interact, + FN_choose, + FN_walk, + FN_walk_to_anim, + FN_turn, + FN_stand_at, + FN_stand, + FN_stand_after_anim, + FN_pause, + FN_mega_table_anim, + FN_add_menu_object, + FN_start_conversation, + FN_end_conversation, + FN_set_frame, + FN_random_pause, + FN_register_frame, + FN_no_sprite, + FN_send_sync, + FN_update_player_stats, + FN_pass_graph, + FN_init_floor_mouse, + FN_pass_mega, + FN_face_xy, + FN_end_session, + FN_no_human, + FN_add_human, + FN_we_wait, + FN_they_do_we_wait, + FN_they_do, + FN_walk_to_talk_to_mega, + FN_fade_down, + FN_i_speak, + FN_total_restart, + FN_set_walkgrid, + FN_speech_process, + FN_set_scaling, + FN_start_event, + FN_check_event_waiting, + FN_request_speech, + FN_gosub, + FN_timed_wait, + FN_play_fx, + FN_stop_fx, + FN_play_music, + FN_stop_music, + FN_set_value, + FN_new_script, + FN_get_sync, + FN_wait_sync, + FN_register_walkgrid, + FN_reverse_mega_table_anim, + FN_reverse_anim, + FN_add_to_kill_list, + FN_set_standby_coords, + FN_back_par0_sprite, + FN_back_par1_sprite, + FN_fore_par0_sprite, + FN_fore_par1_sprite, + FN_set_player_action_event, + FN_set_scroll_coordinate, + FN_stand_at_anim, + FN_set_scroll_left_mouse, + FN_set_scroll_right_mouse, + FN_colour, + FN_flash, + FN_prefetch, + FN_get_player_savedata, + FN_pass_player_savedata, + FN_send_event, + FN_add_walkgrid, + FN_remove_walkgrid, + FN_check_for_event, + FN_pause_for_event, + FN_clear_event, + FN_face_mega, + FN_play_sequence, + FN_shaded_sprite, + FN_unshaded_sprite, + FN_fade_up, + FN_display_msg, + FN_set_object_held, + FN_add_sequence_text, + FN_reset_globals, + FN_set_palette, + FN_register_pointer_text, + FN_fetch_wait, + FN_release, + FN_prepare_music, + FN_sound_fetch, + FN_prepare_music, + FN_smacker_lead_in, + FN_smacker_lead_out, + FN_stop_all_fx, + FN_check_player_activity, + FN_reset_player_activity_delay, + FN_check_music_playing, + FN_play_credits, + FN_set_scroll_speed_normal, + FN_set_scroll_speed_slow, + FN_remove_chooser, + FN_set_fx_vol_and_pan, + FN_set_fx_vol, + FN_restore_game, + FN_refresh_inventory, + FN_change_shadows, +}; + +#define CHECKSTACKPOINTER2 ASSERT((stackPointer2>=0)&&(stackPointer2<STACK_SIZE)); +#define PUSHONSTACK(x) {stack2[stackPointer2] = (x);stackPointer2++;CHECKSTACKPOINTER2;} +#define POPOFFSTACK(x) {x=stack2[stackPointer2-1];stackPointer2--;CHECKSTACKPOINTER2;} +#define DOOPERATION(x) {stack2[stackPointer2-2] = (x);stackPointer2--;CHECKSTACKPOINTER2;} + +#ifndef INSIDE_LINC +void SetGlobalInterpreterVariables(int32 *vars) +{ + globalInterpreterVariables2 = vars; +} +#endif + +#ifdef INSIDE_LINC // Are we running in linc? +int RunScript ( MCBOVirtualSword &engine , const char * scriptData , char * objectData , uint32 *offset ) +#else +int RunScript ( const char * scriptData , char * objectData , uint32 *offset ) +#endif +{ + #define STACK_SIZE 10 + + _standardHeader *header = (_standardHeader *)scriptData; + scriptData += sizeof(_standardHeader) + sizeof(_object_hub); + + // The script data format: + + + // int32_TYPE 1 Size of variable space in bytes + // ... The variable space + // int32_TYPE 1 numberOfScripts + // int32_TYPE numberOfScripts The offsets for each script + + + // Initialise some stuff + + int ip = 0; // Code pointer + int curCommand,parameter,value; // Command and parameter variables + int32 stack2[STACK_SIZE]; // The current stack + int32 stackPointer2 = 0; // Position within stack + int parameterReturnedFromMcodeFunction=0; // Allow scripts to return things + int savedStartOfMcode; // For saving start of mcode commands + + // Get the start of variables and start of code + DEBUG3("Enter interpreter data %x, object %x, offset %d",scriptData,objectData,*offset); + const char *variables = scriptData + sizeof(int); + const char *code = scriptData + *((int *)scriptData) + sizeof(int); + uint32 noScripts = *((int32 *)code); + if ( (*offset) < noScripts) + { ip = ((int *)code)[(*offset)+1]; + DEBUG2("Start script %d with offset %d",*offset,ip); + } + else + { ip = (*offset); + DEBUG1("Start script with offset %d",ip); + } + + code += noScripts * sizeof(int) + sizeof(int); + +/************************************************************************************************/ +#ifdef DONTPROCESSSCRIPTCHECKSUM + + code += sizeof(int) * 3; + +#else + + // Code should nopw be pointing at an identifier and a checksum + int *checksumBlock = (int *)code; + code += sizeof(int) * 3; + + if (checksumBlock[0] != 12345678) + { +#ifdef INSIDE_LINC + AfxMessageBox(CVString("Invalid script in object %s",header->name)); +#else + Con_fatal_error("Invalid script in object %s",header->name); +#endif + return(0); + } + int codeLen = checksumBlock[1]; + int checksum = 0; + for (int count = 0 ; count < codeLen ; count++) + checksum += (unsigned char)code[count]; + if ( checksum != checksumBlock[2] ) + { +#ifdef INSIDE_LINC + AfxMessageBox(CVString("Checksum error in script %s",header->name)); +#else + Con_fatal_error("Checksum error in object %s",header->name); +#endif + return(0); + } + +#endif //DONTPROCESSSCRIPTCHECKSUM + +/************************************************************************************************/ + + int runningScript = 1; + while ( runningScript ) + { curCommand = code[ip++]; + switch(curCommand) + { case CP_END_SCRIPT: // 0 End the script + DEBUG1("End script",0); + runningScript = 0; +#ifdef INSIDE_LINC + engine.AddTextLine( "End script" , VS_COL_GREY ); + engine.AddTextLine( "" , VS_COL_GREY ); +#endif + break; + + case CP_PUSH_LOCAL_VAR32: // 1 Push the contents of a local variable + Read16ip(parameter) + DEBUG2("Push local var %d (%d)",parameter,*((int *)(variables+parameter))); + PUSHONSTACK ( *((int *)(variables+parameter)) ); + break; + + + case CP_PUSH_GLOBAL_VAR32: // 2 Push a global variable + Read16ip(parameter) +#ifdef INSIDE_LINC + DEBUG2("Push global var %d (%d)",parameter,g_GlobalVariables.GetLocalByIndex(parameter).GetValue()); + PUSHONSTACK ( g_GlobalVariables.GetLocalByIndex(parameter).GetValue() ); +#else + DEBUG2("Push global var %d (%d)",parameter,globalInterpreterVariables2[parameter]); + ASSERT(globalInterpreterVariables2); + PUSHONSTACK ( globalInterpreterVariables2[parameter] ); +#endif + break; + + case CP_POP_LOCAL_VAR32: // 3 Pop a value into a local word variable + Read16ip(parameter) + POPOFFSTACK ( value ); + DEBUG2("Pop %d into var %d",value,parameter); + *((int *)(variables+parameter)) = value; + break; + + case CP_CALL_MCODE: // 4 Call an mcode routine + { + Read16ip(parameter) + ASSERT(parameter <= MAX_FN_NUMBER); + value = *((int8 *)(code+ip)); // amount to adjust stack by (no of parameters) + ip ++; + DEBUG2("Call mcode %d with stack = %x",parameter,stack2+(stackPointer2-value)); +#ifdef INSIDE_LINC + int retVal = engine.McodeTable(parameter , stack2+(stackPointer2-value)); +#else + int retVal = McodeTable[parameter](stack2+(stackPointer2-value)); +#endif + stackPointer2 -= value; + CHECKSTACKPOINTER2 + switch ( retVal & 7 ) + { case IR_STOP: // 0: Quit out for a cycle + *offset = ip; + return(0); + + case IR_CONT: // 1: // Continue as normal + break; + + case IR_TERMINATE: // 2: + // Return without updating the offset + return(2); + + case IR_REPEAT: // 3: + // Return setting offset to start of this function call + *offset = savedStartOfMcode; + return(0); + + case IR_GOSUB: // 4: //that's really neat + *offset = ip; + return(2); + + default: + ASSERT(FALSE); + } + parameterReturnedFromMcodeFunction = retVal >> 3; + } + break; + + case CP_PUSH_LOCAL_ADDR: // 5 push the address of a local variable + Read16ip(parameter) + DEBUG2("Push address of local variable %d (%x)",parameter,(int32)(variables + parameter)); + PUSHONSTACK ( (int32)(variables + parameter) ); + break; + + case CP_PUSH_INT32: // 6 Push a long word value on to the stack + Read32ip(parameter) + DEBUG2("Push int32 %d (%x)",parameter,parameter); + PUSHONSTACK ( parameter ); + break; + + + case CP_SKIPONFALSE: // 7 Skip if the value on the stack is false + Read32ipLeaveip(parameter) + POPOFFSTACK ( value ); + DEBUG2("Skip %d if %d is false",parameter,value); + if (value) + ip += sizeof(int32); + else + ip += parameter; + break; + + case CP_SKIPALLWAYS: // 8 skip a block + Read32ipLeaveip(parameter) + DEBUG1("Skip %d",parameter); + ip += parameter; + break; + + case CP_SWITCH: // 9 switch + { POPOFFSTACK ( value ); + int caseCount; + Read32ip(caseCount) + // Search the cases + int foundCase = 0; + for (int count = 0 ; (count < caseCount) && (!foundCase) ; count++) + { + if (value == *((int32 *)(code+ip))) + { // We have found the case, so lets jump to it + foundCase = 1; + ip += *((int32 *)(code+ip+sizeof(int32))); + } + else + ip += sizeof(int32) * 2; + } + // If we found no matching case then use the default + if (!foundCase) + { + ip += *((int32 *)(code+ip)); + } + } + break; + + case CP_ADDNPOP_LOCAL_VAR32: // 10 + Read16ip(parameter) + POPOFFSTACK ( value ); + *((int *)(variables+parameter)) += value; + DEBUG3("+= %d into var %d->%d",value,parameter,*((int *)(variables+parameter))); + break; + + case CP_SUBNPOP_LOCAL_VAR32: // 11 + Read16ip(parameter) + POPOFFSTACK ( value ); + *((int *)(variables+parameter)) -= value; + DEBUG3("-= %d into var %d->%d",value,parameter,*((int *)(variables+parameter))); + break; + + case CP_SKIPONTRUE: // 12 Skip if the value on the stack is TRUE + Read32ipLeaveip(parameter) + POPOFFSTACK ( value ); + DEBUG2("Skip %d if %d is false",parameter,value); + if (!value) + ip += sizeof(int32); + else + ip += parameter; + break; + + case CP_POP_GLOBAL_VAR32: // 13 // Pop a global variable + Read16ip(parameter) + POPOFFSTACK ( value ); + DEBUG2("Pop %d into global var %d",value,parameter); +#ifdef INSIDE_LINC + g_GlobalVariables.lclSet(parameter,value); + engine.AddTextLine(CVString( "Set variable %s to %d", + g_GlobalVariables.GetLocalByIndex(parameter).GetName(), + g_GlobalVariables.GetLocalByIndex(parameter).GetValue() ), + VS_COL_GREY); +#else //INSIDE_LINC + +#ifdef TRACEGLOBALVARIABLESET + TRACEGLOBALVARIABLESET(parameter,value); +#endif + + globalInterpreterVariables2[parameter] = value; + +#endif + break; + + case CP_ADDNPOP_GLOBAL_VAR32: // 14 Add and pop a global variable + { Read16ip(parameter) +// parameter = *((int16_TYPE *)(code+ip)); +// ip += 2; + POPOFFSTACK ( value ); +#ifdef INSIDE_LINC + int newVal = g_GlobalVariables.GetLocalByIndex(parameter).GetValue() + value ; + g_GlobalVariables.lclSet(parameter, newVal ); + engine.AddTextLine( CVString( "Set variable %s to %d", + g_GlobalVariables.GetLocalByIndex(parameter).GetName(), + g_GlobalVariables.GetLocalByIndex(parameter).GetValue() ), + VS_COL_GREY); +#else + globalInterpreterVariables2[parameter] += value; + DEBUG3("+= %d into global var %d->%d",value,parameter,*((int *)(variables+parameter))); +#endif + break; + } + + case CP_SUBNPOP_GLOBAL_VAR32: // 15 Sub and pop a global variable + { Read16ip(parameter) +// parameter = *((int16_TYPE *)(code+ip)); +// ip += 2; + POPOFFSTACK ( value ); +#ifdef INSIDE_LINC + int newVal = g_GlobalVariables.GetLocalByIndex(parameter).GetValue() - value ; + g_GlobalVariables.lclSet(parameter, newVal ); + engine.AddTextLine( CVString( "Set variable %s to %d", + g_GlobalVariables.GetLocalByIndex(parameter).GetName(), + g_GlobalVariables.GetLocalByIndex(parameter).GetValue() ), + VS_COL_GREY); +#else + globalInterpreterVariables2[parameter] -= value; + DEBUG3("-= %d into global var %d->%d",value,parameter,*((int *)(variables+parameter))); +#endif + break; + } + + case CP_DEBUGON: + // Turn debugging on + g_debugFlag = 1; + break; + + case CP_DEBUGOFF: + // Turn debugging on + g_debugFlag = 0; + break; + + case CP_QUIT: +#ifdef INSIDE_LINC + break; +#else + // Quit out for a cycle + *offset = ip; + return(0); +#endif + + case CP_TERMINATE: + // Quit out immediately without affecting the offset pointer + return(3); + +/****************************************************************************************************************** +******************************************************************************************************************/ + + // Operators + + case OP_ISEQUAL: // 20 // '==' + DEBUG3("%d == %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] == stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] == stack2[stackPointer2-1]) ); + break; + + case OP_PLUS: // 21 // '+' + DEBUG3("%d + %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] + stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] + stack2[stackPointer2-1]) ); + break; + + case OP_MINUS: // 22 // '+' + DEBUG3("%d - %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] - stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] - stack2[stackPointer2-1]) ); + break; + + case OP_TIMES: // 23 // '+' + DEBUG3("%d * %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] * stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] * stack2[stackPointer2-1]) ); + break; + + case OP_DEVIDE: // 24 // '+' + DEBUG3("%d / %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] / stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] / stack2[stackPointer2-1]) ); + break; + + case OP_NOTEQUAL: // 25 // '!=' + DEBUG3("%d != %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] != stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] != stack2[stackPointer2-1]) ); + break; + + case OP_ANDAND: // 26 + DEBUG3("%d != %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] && stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] && stack2[stackPointer2-1]) ); + break; + + case OP_GTTHAN: // 27 > + DEBUG3("%d > %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] > stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] > stack2[stackPointer2-1]) ); + break; + + case OP_LSTHAN: // 28 < + DEBUG3("%d < %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] < stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] < stack2[stackPointer2-1]) ); + break; + + case CP_JUMP_ON_RETURNED: // 29 + { // Jump to a part of the script depending on the return value from an mcode routine + parameter = *((int8 *)(code+ip)); // Get the maximum value + ip++; +#ifdef INSIDE_LINC + TRACE("ip %d: Parameter %d skip %d\r\n", ip, + parameterReturnedFromMcodeFunction, + ((int32*)(code+ip))[parameterReturnedFromMcodeFunction] ); +#endif + + ip += ((int32 *)(code+ip))[parameterReturnedFromMcodeFunction]; + } + break; + + case CP_TEMP_TEXT_PROCESS: // 30 + // Process a text line + Read32ip(parameter) +// parameter = *((int32_TYPE *)(code+ip)); +// ip += sizeof(int32_TYPE);; + DEBUG1("Process text id %d",parameter); +#ifdef INSIDE_LINC + // Linc only for the moment + engine.ProcessTextLine(parameter); +#endif //INSIDE_LINC + break; + + case CP_SAVE_MCODE_START: // 31 + // Save the start position on an mcode instruction in case we need to restart it again + savedStartOfMcode = ip-1; + break; + + case CP_RESTART_SCRIPT: // 32 + { // Start the script again + // Do a ip search to find the script we are running + const char *tempScrPtr = scriptData + *((int *)scriptData) + sizeof(int); + int scriptNumber = 0; + int foundScript = 0; + uint32 count = 0; + for (count = 1 ; (count < noScripts) && (!foundScript) ; count++) + { if (ip < ((int *)tempScrPtr)[count+1]) + { scriptNumber = count - 1 ; + foundScript = 1; + } + } + if (!foundScript) + scriptNumber = count - 1 ; + // So we know what script we are running, lets restart it + ip = ((int *)tempScrPtr)[scriptNumber+1]; + break; + } + + case CP_PUSH_STRING: // 33 + { // Push the address of a string on to the stack + parameter = *((int8 *)(code+ip)); // Get the string size + ip += 1; + // ip points to the string + PUSHONSTACK( (int)(code+ip) ); + ip += (parameter+1); + break; + } + + case CP_PUSH_DEREFERENCED_STRUCTURE: // 34 + { // Push the address of a dereferenced structure + Read32ip(parameter) + DEBUG1("Push address of far variable (%x)",(int32)(variables + parameter)); + PUSHONSTACK( (int)(objectData + sizeof(int) + sizeof(_standardHeader) + sizeof(_object_hub) + parameter)); + break; + } + + case OP_GTTHANE: // 35 >= + DEBUG3("%d > %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] >= stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] >= stack2[stackPointer2-1]) ); + break; + + case OP_LSTHANE: // 36 <= + DEBUG3("%d < %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] <= stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] <= stack2[stackPointer2-1]) ); + break; + + case OP_OROR: // 37 + DEBUG3("%d || %d -> %d", stack2[stackPointer2-2], + stack2[stackPointer2-1], + stack2[stackPointer2-2] || stack2[stackPointer2-1]); + DOOPERATION ( (stack2[stackPointer2-2] || stack2[stackPointer2-1]) ); + break; + + + default: +#ifdef INSIDE_LINC + AfxMessageBox(CVString("Invalid interpreter token %d",curCommand)); +#else + Con_fatal_error("Interpreter error: Invalid token %d", curCommand); +#endif + return(3); + } + + } + + return(1); +} diff --git a/sword2/interpreter.h b/sword2/interpreter.h new file mode 100644 index 0000000000..2f28ca83f9 --- /dev/null +++ b/sword2/interpreter.h @@ -0,0 +1,173 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +// Interpreter return codes + +#define IR_STOP 0 +#define IR_CONT 1 +#define IR_TERMINATE 2 +#define IR_REPEAT 3 +#define IR_GOSUB 4 + + +#ifdef INSIDE_LINC // Are we running in linc? + +extern int g_debugFlag; + +#ifdef _DEBUG + +#define DEBUG1(x,y) if(g_debugFlag){engine.AddTextLine(CVString(x,y),VS_COL_DEBUG);} +#define DEBUG2(x,y,z) if(g_debugFlag){engine.AddTextLine(CVString(x,y,z),VS_COL_DEBUG);} +#define DEBUG3(x,y,z,a) if(g_debugFlag){engine.AddTextLine(CVString(x,y,z,a),VS_COL_DEBUG);} + +#else //_DEBUG + +#define DEBUG1 +#define DEBUG2 +#define DEBUG3 + +#endif //_DEBUG + +#else //INSIDE_LINC + +//#include "src\driver96.h" +#include "debug.h" +#include "header.h" + +#define DEBUG1 if(g_debugFlag)Zdebug +#define DEBUG2 if(g_debugFlag)Zdebug +#define DEBUG3 if(g_debugFlag)Zdebug + +#define ASSERT(x) {if(!(x)){Zdebug("Interpreter ASSERT %s,%d",__FILE__,__LINE__);Con_fatal_error("Assert error in interpreter");}} + + +#endif + + + // Get parameter fix so that the playstation version can handle words not on word boundaries +#define Read16ip(var) {var = *((int16 *)(code+ip));ip+=sizeof(int16);} +#define Read32ip(var) {var = *((int32 *)(code+ip));ip+=sizeof(int32);} +#define Read32ipLeaveip(var) {var = *((int32 *)(code+ip));} + +void SetGlobalInterpreterVariables(int32 *vars); + +#ifdef INSIDE_LINC // Are we running in linc? +int RunScript ( MCBOVirtualSword &engine , const char * scriptData , char * /*objectData*/ , uint32 *offset ); +#else +int RunScript ( const char * scriptData , char * /*objectData*/ , uint32 *offset ); +#endif + + + +// Command tokens + +#define CT_COMMENT 1 // A program comment +#define CT_IF 2 // An if statement +#define CT_OPENBRACKET 3 // ( +#define CT_CLOSEBRACKET 4 // ) +#define CT_VAR 5 // Define a variable +#define CT_SEMICOLON 6 // ; +#define CT_COMMA 7 // , +#define CT_OPENBRACE 8 // { +#define CT_CLOSEBRACE 9 // } +#define CT_STRUCT 10 // Struct +#define CT_SWITCH 11 // Switch +#define CT_CASE 12 // Case +#define CT_BREAK 13 // break +#define CT_DEFAULT 14 // default +#define CT_ASSIGN 14 // = +#define CT_PLUSEQ 15 // '+=' +#define CT_MINUSEQ 16 // '-=' +#define CT_FOR 17 // for +#define CT_DO 18 // do +#define CT_WHILE 19 // while +#define CT_DEBUGON 20 // Turn debugging on +#define CT_DEBUGOFF 21 // Turn debugging off +#define CT_QUIT 22 // Quit for a cycle +#define CT_ENDIF 23 // Endif +#define CT_TEXTOBJECT 24 // Speaker: text line +#define CT_ANIM 25 // An animation +#define CT_ELSE 26 // else to an if +#define CT_CHOOSE 27 // Start a chooser +#define CT_END 28 // end, usually followed by something else +#define CT_END_CHOICE 29 // end choice +#define CT_TERMINATE 30 // Terminate +#define CT_PAUSE 31 // Pause +#define CT_RESTART 32 // Restart script +#define CT_START 33 // Start conversation +#define CT_CALL 34 // Call a character +#define CT_ACTORSCOMMENT 35 // A comment for an actor +#define CT_TALKER 36 // A set talker command + +// Special functions + +#define SF_RUNLIST 1 +#define SF_DOUBLEQUOTE 2 +#define SF_BACKGROUND 3 +#define SF_SCALEA 4 +#define SF_SCALEB 5 +#define SF_SPEECHSCRIPT 6 + +// Compiled tokens + +#define CP_END_SCRIPT 0 +#define CP_PUSH_LOCAL_VAR32 1 // Push a local variable on to the stack +#define CP_PUSH_GLOBAL_VAR32 2 // Push a global variable +#define CP_POP_LOCAL_VAR32 3 // Pop a local variable from the stack +#define CP_CALL_MCODE 4 // Call a machine code function +#define CP_PUSH_LOCAL_ADDR 5 // Push the address of a local variable +#define CP_PUSH_INT32 6 // Adjust the stack after calling an fn function +#define CP_SKIPONFALSE 7 // Skip if the bottom value on the stack is false +#define CP_SKIPALLWAYS 8 // Skip a block of code +#define CP_SWITCH 9 // Switch on last stack value +#define CP_ADDNPOP_LOCAL_VAR32 10 // Add to a local varible +#define CP_SUBNPOP_LOCAL_VAR32 11 // Subtract to a local variable +#define CP_SKIPONTRUE 12 // Skip if the bottom value on the stack is true +#define CP_POP_GLOBAL_VAR32 13 // Pop a global variable +#define CP_ADDNPOP_GLOBAL_VAR32 14 +#define CP_SUBNPOP_GLOBAL_VAR32 15 +#define CP_DEBUGON 16 // Turn debugging on +#define CP_DEBUGOFF 17 // Turn debugging off +#define CP_QUIT 18 // Quit for a cycle +#define CP_TERMINATE 19 // Quit script completely + +// Operators + +#define OP_ISEQUAL 20 // '==' +#define OP_PLUS 21 // '+' +#define OP_MINUS 22 // '-' +#define OP_TIMES 23 // '*' +#define OP_DEVIDE 24 // '/' +#define OP_NOTEQUAL 25 // '==' +#define OP_ANDAND 26 // && +#define OP_GTTHAN 27 // > +#define OP_LSTHAN 28 // < + +// More tokens, mixed types + +#define CP_JUMP_ON_RETURNED 29 // Use table of jumps with value returned from fn_mcode +#define CP_TEMP_TEXT_PROCESS 30 // A dummy text process command for me +#define CP_SAVE_MCODE_START 31 // Save the mcode code start for restarting when necessary +#define CP_RESTART_SCRIPT 32 // Start the script from the beginning +#define CP_PUSH_STRING 33 // Push a pointer to a string on the stack +#define CP_PUSH_DEREFERENCED_STRUCTURE 34 // Push the address of a structure thing + +#define OP_GTTHANE 35 // >= +#define OP_LSTHANE 36 // <= +#define OP_OROR 37 // || or OR diff --git a/sword2/layers.cpp b/sword2/layers.cpp new file mode 100644 index 0000000000..0cf405407f --- /dev/null +++ b/sword2/layers.cpp @@ -0,0 +1,297 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +//high level layer initialising + +//the system supports: +// 1 optional background parallax layer +// 1 not optional normal backdrop layer +// 3 normal sorted layers +// up to 2 foreground parallax layers + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "driver/driver96.h" +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "header.h" +#include "layers.h" +#include "memory.h" +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "sound.h" // (James22july97) for Clear_fx_queue() called from FN_init_background() + +//------------------------------------------------------------------------------------ + + +screen_info this_screen; //this_screen describes the current back buffer and its in-game scroll positions, etc. +//------------------------------------------------------------------------------------ +int32 FN_init_background(int32 *params) //Tony11Sept96 +{ +//param 0 res id of normal background layer - cannot be 0 +//param 1 1 yes 0 no for a new palette +//this screen defines the size of the back buffer + + _multiScreenHeader *screenLayerTable; // James 06feb97 + _screenHeader *screen_head; + _layerHeader *layer; + _spriteInfo spriteInfo; + uint32 j; + uint8 *file; + uint32 rv; + + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"====================================="); + Zdebug(0,"CHANGED TO LOCATION \"%s\"", FetchObjectName(*params)); + Zdebug(0,"====================================="); + + // Also write this to system debug file + Zdebug("====================================="); + Zdebug("CHANGED TO LOCATION \"%s\"", FetchObjectName(*params)); + Zdebug("====================================="); + #endif + //-------------------------------------- + + Clear_fx_queue(); // stops all fx & clears the queue (James22july97) + + +#ifdef _DEBUG + Zdebug("FN_init_background(%d)", *params); + + if (!*params) + { + Con_fatal_error("ERROR: FN_set_background cannot have 0 for background layer id! (%s line=%u)",__FILE__,__LINE__); + } +#endif // _DEBUG + + + //------------------------------------------------------- + // if the screen is still fading down then wait for black + do + { + ServiceWindows(); + } + while(GetFadeStatus()==RDFADE_DOWN); + //------------------------------------------------------- + + if (this_screen.mask_flag) // if last screen was using a shading mask (see below) (James 08apr97) + { + rv = CloseLightMask(); + + if (rv) + ExitWithReport("Driver Error %.8x [%s line %u]", rv, __FILE__, __LINE__); + } + + //-------------------------------------------------------- + // New stuff for faster screen drivers (James 06feb97) + + if (this_screen.background_layer_id) // for drivers: close the previous screen if one is open + CloseBackgroundLayer(); + + //-------------------------------------------------------- + + + this_screen.background_layer_id=*params; //set the res id + this_screen.new_palette = *(params+1); //yes or no - palette is taken from layer file + + +//ok, now read the resource and pull out all the normal sort layer info +//and set them up at the beginning of the sort list - why do it each cycle + + + file = res_man.Res_open(this_screen.background_layer_id); //file points to 1st byte in the layer file + + screen_head = FetchScreenHeader(file); + + this_screen.number_of_layers= screen_head->noLayers; //set number of special sort layers + this_screen.screen_wide = screen_head->width; + this_screen.screen_deep = screen_head->height; + + Zdebug("res test layers=%d width=%d depth=%d", screen_head->noLayers, screen_head->width, screen_head->height); + + SetLocationMetrics(screen_head->width, screen_head->height); //initialise the driver back buffer + + + if (screen_head->noLayers) + for (j=0;j<screen_head->noLayers;j++) + { + layer=FetchLayerHeader(file,j); //get layer header for layer j + +// add into the sort list + + sort_list[j].sort_y = layer->y+layer->height; //need this for sorting - but leave the rest blank, we'll take from the header at print time + sort_list[j].layer_number=j+1; //signifies a layer + + Zdebug("init layer %d", j); + } + + + +//using the screen size setup the scrolling variables + + if( ((screen_head->width) > screenWide) || (screen_head->height>screenDeep) ) // if layer is larger than physical screen + { + this_screen.scroll_flag = 2; //switch on scrolling (2 means first time on screen) + +// note, if we've already set the player up then we could do the initial scroll set here + + this_screen.scroll_offset_x = 0; //reset scroll offsets + this_screen.scroll_offset_y = 0; + +// calc max allowed offsets (to prevent scrolling off edge) - MOVE TO NEW_SCREEN in GTM_CORE.C !! + this_screen.max_scroll_offset_x = screen_head->width-screenWide; // NB. min scroll offsets are both zero + this_screen.max_scroll_offset_y = screen_head->height-(screenDeep-(RDMENU_MENUDEEP*2)); // 'screenDeep' includes the menu's, so take away 80 pixels + } + else //layer fits on physical screen - scrolling not required + { + this_screen.scroll_flag = 0; //switch off scrolling + this_screen.scroll_offset_x = 0; //reset scroll offsets + this_screen.scroll_offset_y = 0; + } + + ResetRenderEngine(); //no inter-cycle scrol between new screens (see setScrollTarget in build display) + + // these are the physical screen coords where the system + // will try to maintain George's actual feet coords + this_screen.feet_x=320; + this_screen.feet_y=340; + + + //---------------------------------------------------- + // shading mask + + screenLayerTable = (_multiScreenHeader *) ((uint8 *) file + sizeof(_standardHeader)); + + if (screenLayerTable->maskOffset) + { + spriteInfo.x = 0; + spriteInfo.y = 0; + spriteInfo.w = screen_head->width; + spriteInfo.h = screen_head->height; + spriteInfo.scale = 0; + spriteInfo.scaledWidth = 0; + spriteInfo.scaledHeight = 0; + spriteInfo.type = 0; + spriteInfo.blend = 0; + spriteInfo.data = FetchShadingMask(file); + spriteInfo.colourTable = 0; + + rv = OpenLightMask( &spriteInfo ); + if (rv) + ExitWithReport("Driver Error %.8x [%s line %u]", rv, __FILE__, __LINE__); + + this_screen.mask_flag=1; // so we know to close it later! (see above) + } + else + this_screen.mask_flag=0; // no need to close a mask later + + //---------------------------------------------------- + + res_man.Res_close(this_screen.background_layer_id); //close the screen file + + SetUpBackgroundLayers(); + + + Zdebug("end init"); + return(1); +} +//------------------------------------------------------------------------------------ +// called from FN_init_background & also from control panel + +void SetUpBackgroundLayers(void) // James(13jun97) +{ + _multiScreenHeader *screenLayerTable; // James 06feb97 + _screenHeader *screen_head; + uint8 *file; + + + if (this_screen.background_layer_id) // if we actually have a screen to initialise (in case called from control panel) + { + //------------------------------ + // open resource & set pointers to headers + + file = res_man.Res_open(this_screen.background_layer_id); //file points to 1st byte in the layer file + + screen_head = FetchScreenHeader(file); + + screenLayerTable = (_multiScreenHeader *) ((uint8 *) file + sizeof(_standardHeader)); + + //------------------------------ + // first background parallax + + if (screenLayerTable->bg_parallax[0]) + InitialiseBackgroundLayer(FetchBackgroundParallaxLayer(file,0)); + else + InitialiseBackgroundLayer(NULL); + + //------------------------------ + // second background parallax + + if (screenLayerTable->bg_parallax[1]) + InitialiseBackgroundLayer(FetchBackgroundParallaxLayer(file,1)); + else + InitialiseBackgroundLayer(NULL); + + //------------------------------ + // normal backround layer + + InitialiseBackgroundLayer(FetchBackgroundLayer(file)); + + //------------------------------ + // first foreground parallax + + if (screenLayerTable->fg_parallax[0]) + InitialiseBackgroundLayer(FetchForegroundParallaxLayer(file,0)); + else + InitialiseBackgroundLayer(NULL); + + //------------------------------ + // second foreground parallax + + if (screenLayerTable->fg_parallax[1]) + InitialiseBackgroundLayer(FetchForegroundParallaxLayer(file,1)); + else + InitialiseBackgroundLayer(NULL); + + //---------------------------------------------------- + + res_man.Res_close(this_screen.background_layer_id); //close the screen file + + //---------------------------------------------------- + } + else // no current screen to initialise! (In case called from control panel) + { + } +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ diff --git a/sword2/layers.h b/sword2/layers.h new file mode 100644 index 0000000000..8bc73b7dda --- /dev/null +++ b/sword2/layers.h @@ -0,0 +1,61 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _LAYERS +#define _LAYERS + +//#include "src\driver96.h" +#include "memory.h" + + + + + + + + +typedef struct +{ + uint16 scroll_offset_x; // position x + uint16 scroll_offset_y; // position y + uint16 max_scroll_offset_x; // calc'ed in FN_init_background + uint16 max_scroll_offset_y; // + int16 player_feet_x; // feet coordinates to use - cant just fetch the player compact anymore + int16 player_feet_y; + int16 feet_x; // special offset-to-player position - tweek as desired - always set in screen manager object startup + int16 feet_y; + uint16 screen_wide; // size of background layer - hense size of back buffer itself (Paul actually malloc's it) + uint16 screen_deep; + uint32 background_layer_id; //id of the normal background layer + uint16 number_of_layers; // from the header of the main background layer + uint8 new_palette; // set to non zero to start the palette held within layer file fading up after a build_display + uint8 scroll_flag; // scroll mode 0 off 1 on + uint8 mask_flag; // using shading mask +} screen_info; + + +extern screen_info this_screen; + + +int32 FN_init_background(int32 *params); // Tony11Sept96 +void SetUpBackgroundLayers(void); // James(13jun97) called from control panel (as well as inside FN_init_background) + + + +#endif diff --git a/sword2/logic.cpp b/sword2/logic.cpp new file mode 100644 index 0000000000..4519badee8 --- /dev/null +++ b/sword2/logic.cpp @@ -0,0 +1,441 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +// #include <libsn.h> PSX? +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +//#include "src\driver96.h" +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "header.h" +#include "interpreter.h" +#include "logic.h" +#include "memory.h" +#include "resman.h" +#include "router.h" // for ClearWalkGridList() +#include "sound.h" +#include "sword2.h" // (James19aug97) for CloseGame() +#include "sync.h" + +//------------------------------------------------------------------------------------ +logic LLogic; //declare the object + +#define LEVEL cur_object_hub->logic_level + +#define OBJECT_KILL_LIST_SIZE 50 // this must allow for the largest number of objects in a screen + +uint32 object_kill_list[OBJECT_KILL_LIST_SIZE]; +uint32 kills=0; // keeps note of no. of objects in the kill list + +//------------------------------------------------------------------------------------ +int logic::Process_session(void) //Tony6June96 (first run 21Oct96) +{ +//do one cycle of the current session + + + + uint32 run_list; + uint32 ret,script; + uint32 *game_object_list; + char *raw_script_ad; + char *raw_data_ad; + uint32 null_pc; + _standardHeader *head; + _standardHeader *far_head; + uint32 id; + + run_list=current_run_list; //might change during the session, so take a copy here + pc=0; //point to first object in list + + static uint32 cycle=0; + + + cycle++; +// Zdebug("\n CYCLE %d", cycle); + + while(pc!=0xffffffff) //by minusing the pc we can cause an immediate cessation of logic processing on the current list + { + head = (_standardHeader*) res_man.Res_open(run_list); + if (head->fileType!=RUN_LIST) + Con_fatal_error("Logic_engine %d not a run_list", run_list); + + game_object_list = (uint32 *) (head+1); + ID = game_object_list[pc++]; //read the next id + id=ID; + res_man.Res_close(run_list); //release the list again so it can float in memory - at this point not one thing should be locked + + +// Zdebug("%d", ID); + + if (!ID) //null terminated + return(0); //end the session naturally + + head = (_standardHeader*) res_man.Res_open(ID); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("Logic_engine %d not an object", ID); + + cur_object_hub = (_object_hub *) (head+1); + +// Zdebug(" %d id(%d) pc(%d)", cur_object_hub->logic_level, cur_object_hub->script_id[cur_object_hub->logic_level], cur_object_hub->script_pc[cur_object_hub->logic_level]); + + + +// do the logic for this object +// we keep going until a function says to stop - remember, system operations are run via function calls to drivers now + do + { + script = cur_object_hub->script_id[LEVEL]; //get the script id as we may be running a script from another object... + + +// there is a distinction between running one of our own scripts and that of another object + if ((script/SIZE)==ID) //its our script + { +// Zdebug("run script %d pc%d", script/SIZE, cur_object_hub->script_pc[LEVEL]); + +// raw_script_ad = (char *) (cur_object_hub+1); //this is the script data + + raw_script_ad = (char*) head; + + ret=RunScript( raw_script_ad, raw_script_ad, &cur_object_hub->script_pc[LEVEL] ); //script and data object are us/same + + } + else //we're running the script of another game object - get our data object address + { +// get the foreign objects script data address + + raw_data_ad=(char*)head; + + far_head = (_standardHeader*) res_man.Res_open(script/SIZE); + if ((far_head->fileType!=GAME_OBJECT)&&((far_head->fileType!=SCREEN_MANAGER))) + Con_fatal_error("Logic_engine %d not a far object (its a %d)", script/SIZE, far_head->fileType); + +// raw_script_ad = (char*) (head+1) + sizeof(_standardHeader); + +// get our objects data address +// raw_data_ad = (char*) (cur_object_hub+1); + + raw_script_ad=(char*)far_head; + + ret=RunScript( raw_script_ad, raw_data_ad, &cur_object_hub->script_pc[LEVEL] ); + + res_man.Res_close(script/SIZE); //close foreign object again + + raw_script_ad=raw_data_ad; //reset to us for service script + } + + if (ret==1) //this script has finished - drop down a level + { + if (cur_object_hub->logic_level) //check that it's not already on level 0 ! + cur_object_hub->logic_level--; + else //Hmmm, level 0 terminated :-| Let's be different this time and simply let it restart next go :-) + { + cur_object_hub->script_pc[LEVEL]=(cur_object_hub->script_id[LEVEL]&0xffff); //reset to rerun +// Zdebug("**WARNING object %d script 0 terminated!", id); + ret=0; //cause us to drop out for a cycle + } + } + else if (ret>2) + { + Con_fatal_error("Process_session: illegal script return type %d (%s line %u)",ret,__FILE__,__LINE__); + } + +// if ret==2 then we simply go around again - a new script or subroutine will kick in and run + + } + while(ret); //keep processing scripts until 0 for quit is returned + + +// any post logic system requests to go here + + Clear_syncs(ID); //clear any syncs that were waiting for this character - it has used them or now looses them + + if (pc!=0xffffffff) //the session is still valid so run the service script + { null_pc=0; + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + } + //made for all live objects + +// and that's it so close the object resource + res_man.Res_close(ID); + }; + + + Process_kill_list(); //leaving a room so remove all ids that must reboot correctly + + Zdebug("RESTART the loop"); + + + return(1); //means restart the loop +} +//------------------------------------------------------------------------------------ +void logic::Express_change_session(uint32 sesh_id) //Tony6June96 +{ +//a game-object can bring an immediate halt to the session and cause a new one to start without a screen update + + current_run_list=sesh_id; //set to new + pc=0xffffffff; //causes session to quit + + EXIT_FADING=0; // reset now in case we double-clicked an exit prior to changing screen + + Init_sync_system(); // we're trashing the list - presumably to change room + // in theory sync waiting in the list could be left behind and never removed - so we trash the lot + + ClearWalkGridList(); // reset walkgrid list (see FN_register_walkgrid) + Clear_fx_queue(); // stops all fx & clears the queue + FreeAllRouteMem(); // free all the route memory blocks from previous game +} +//------------------------------------------------------------------------------------ +void logic::Natural_change_session(uint32 sesh_id) //Tony7June96 +{ +//a new session will begin next game cycle. +//the current cycle will conclude and build the screen and flip into view as normal + + current_run_list=sesh_id; //set to new +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +uint32 logic::Return_run_list(void) //Tony18Sept96 +{ +//pass back the private cur_object_list variable - not sure we need this + + return(current_run_list); //return the id +} +//------------------------------------------------------------------------------------ +int32 FN_set_session(int32 *params) //Tony29Oct96 +{ +//used by player invoked start scripts + +//param 0 id of new run list + LLogic.Express_change_session(*params); //now! + + return(IR_CONT); //cont +} +//------------------------------------------------------------------------------------ +int32 FN_end_session(int32 *params) //Tony21Sept96 +{ +//causes no more objects in this logic loop to be processed +//the logic engine will restart at the beginning of the new list +// !!the current screen will not be drawn!! + +//param 0 id of new run-list + + LLogic.Express_change_session(*params); //terminate current and change to next run-list + + return(0); //stop the script - logic engine will now go around and the new screen will begin +} +//------------------------------------------------------------------------------------ +void logic::Logic_up(uint32 new_script) //Tony23Sept96 +{ +//move the current object up a level +//called by FN_gosub command - remember, only the logic object has access to cur_object_hub + + + cur_object_hub->logic_level++; //going up a level - and we'll keeping going this cycle + + if (cur_object_hub->logic_level==3) //can be 0,1,2 + Con_fatal_error("Logic_up id %d has run off script tree! :-O", ID); + + cur_object_hub->script_id[cur_object_hub->logic_level]=new_script; //setup new script on next level (not the current level) + cur_object_hub->script_pc[cur_object_hub->logic_level]=new_script&0xffff; + + //Zdebug("new pc = %d", new_script&0xffff); + +} +//------------------------------------------------------------------------------------ +void logic::Logic_one(uint32 new_script) //Tony4Dec96 +{ +//force to level one + + cur_object_hub->logic_level=1; + + cur_object_hub->script_id[1]=new_script; //setup new script on level 1 + cur_object_hub->script_pc[1]=new_script&0xffff; + +} +//------------------------------------------------------------------------------------ + +void logic::Logic_replace(uint32 new_script) //Tony13Nov96 +{ +//change current logic - script must quit with a TERMINATE directive - which does not write to &pc + + cur_object_hub->script_id[cur_object_hub->logic_level]=new_script; //setup new script on this level + cur_object_hub->script_pc[cur_object_hub->logic_level]=new_script&0xffff; + +} +//------------------------------------------------------------------------------------ +uint32 logic::Examine_run_list(void) //Tony25Oct96 +{ + uint32 *game_object_list; + _standardHeader *file_header; + int scrolls=0; + char c; + + + if (current_run_list) + { + game_object_list = (uint32 *) (res_man.Res_open(current_run_list)+sizeof(_standardHeader)); //open and lock in place + + Print_to_console("runlist number %d", current_run_list); + + while(*(game_object_list)) + { + file_header = (_standardHeader*) res_man.Res_open(*(game_object_list)); + Print_to_console(" %d %s",*(game_object_list), file_header->name); + res_man.Res_close(*(game_object_list++)); + + scrolls++; + Build_display(); + + if (scrolls==18) + { + Temp_print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(!KeyWaiting()); + + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + + Clear_console_line(); //clear the Press Esc message ready for the new line + scrolls=0; + } + + + } + + + + + res_man.Res_close(current_run_list); + } + else Print_to_console("no run list set"); + + + Scroll_console(); + return(1); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void logic::Total_restart(void) //Tony18Sept96 +{ +//reset the object restart script 1 on level 0 + + cur_object_hub->logic_level=0; + + //cur_object_hub->script_id[0]=1; + cur_object_hub->script_pc[0]=1; //reset to rerun + +} +//------------------------------------------------------------------------------------ +int32 FN_total_restart(int32 *params) //Tony5Dec96 +{ +//mega runs this to restart its base logic again - like being cached in again + + LLogic.Total_restart(); + + if (params); + + return(IR_TERMINATE); //drop out without saving pc and go around again +} +//------------------------------------------------------------------------------------ +int32 FN_add_to_kill_list(int32 *params) //James9jan97 +{ + // call *once* from object's logic script - ie. in startup code + // - so not re-called every time script drops off & restarts! + + // mark this object for killing - to be killed when player leaves this screen + // - so object reloads & script restarts upon re-entry to screen + // - causes this object's startup logic to be re-run every time we enter the screen + // - "which is nice" + + // params: none + + uint32 entry; + + + if (ID != 8) // DON'T EVER KILL GEORGE! + { + // first, scan list to see if this object is already included (05mar97 James) + entry=0; + while ((entry < kills) && (object_kill_list[entry] != ID)) + entry++; + + if (entry == kills) // if this ID isn't already in the list, then add it, (otherwise finish) (05mar97 James) + { + #ifdef _DEBUG + if (kills == OBJECT_KILL_LIST_SIZE) // no room at the inn + Con_fatal_error("List full in FN_add_to_kill_list(%u) (%s line %u)",ID,__FILE__,__LINE__); + #endif + + object_kill_list[kills] = ID; // add this 'ID' to the kill list + kills++; // "another one bites the dust" + + // when we leave the screen, all these object resources are to be cleaned out of memory + // and the kill list emptied by doing 'kills=0' + // - ensuring that all resources are in fact still in memory & more importantly closed + // before killing! + } + } + + return(IR_CONT); // continue script +} +//------------------------------------------------------------------------------------ +void logic::Process_kill_list(void) //Tony10Jan97 +{ + + uint32 j; + + + if (kills) + for (j=0;j<kills;j++) + res_man.Remove_res(object_kill_list[j]); + + + kills=0; + +} +//------------------------------------------------------------------------------------ +void logic::Reset_kill_list(void) //James 25mar97 +{ + kills=0; +} +//------------------------------------------------------------------------------------ + diff --git a/sword2/logic.h b/sword2/logic.h new file mode 100644 index 0000000000..a10f3a6613 --- /dev/null +++ b/sword2/logic.h @@ -0,0 +1,62 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//logic management + +#ifndef _LOGIC +#define _LOGIC + +//#include "src\driver96.h" +#include "defs.h" +#include "header.h" + +#define TREE_SIZE 3 + + + +class logic +{ + public: + + int Process_session(void); //do one cycle of the current session + void Express_change_session(uint32 sesh_id); //cause the logic loop to terminate and drop out + void Natural_change_session(uint32 sesh_id); //new logic begins next cycle + uint32 Return_run_list(void); + void Logic_up(uint32 new_script); //setup script_id and script_pc in cur_object_hub - called by FN_gosub() + void Logic_replace(uint32 new_script); + void Logic_one(uint32 new_script); + void Total_restart(void); + uint32 Examine_run_list(void); + void Reset_kill_list(void); //James 25mar97 + + + private: + + uint32 current_run_list; //denotes the res id of the game-object-list in current use + void Process_kill_list(void); + uint32 pc; //pc during logic loop + _object_hub *cur_object_hub; //each object has one of these tacked onto the beginning + +}; + +extern logic LLogic; + +int32 FN_add_to_kill_list(int32 *params); //James9jan97 + +#endif diff --git a/sword2/maketext.cpp b/sword2/maketext.cpp new file mode 100644 index 0000000000..eb90d7d7cc --- /dev/null +++ b/sword2/maketext.cpp @@ -0,0 +1,694 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// MAKETEXT - Constructs a single-frame text sprite: returns a handle to a +// FLOATING memory block containing the sprite, given a +// null-terminated string, max width allowed, pen colour and +// pointer to required character set. +// +// NB 1) The routine does not create a standard file header or +// an anim header for the text sprite - the data simply begins +// with the frame header. +// +// NB 2) If pen colour is zero, it copies the characters into the +// sprite without remapping the colours. +// ie. It can handle both the standard 2-colour font for speech +// and any multicoloured fonts for control panels, etc. +// +// Based on textsprt.c as used for Broken Sword 1, but updated for new system +// by JEL on 9oct96 and updated again (for font as a resource) on 5dec96. + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#define MAX_LINES 30 // max character lines in output sprite + +#define BORDER_COL 200 // source colour for character border (only needed for remapping colours) +#define LETTER_COL 193 // source colour for bulk of character ( " ) +#define BORDER_PEN 194 // output colour for character border - should be black ( " ) but note that we have to use a different pen number during sequences + +#define NO_COL 0 // sprite background - 0 for transparency! +#define SPACE ' ' +#define FIRST_CHAR SPACE // first character in character set +#define LAST_CHAR 255 // last character in character set +#define DUD 64 // the first "chequered flag" (dud) symbol in our character set is in the '@' position +//----------------------------------------------------------------------------- +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "driver/driver96.h" +#include "console.h" +#include "debug.h" +#include "defs.h" // for SPEECH_FONT_ID & CONSOLE_FONT_ID +#include "header.h" +#include "maketext.h" +#include "memory.h" +#include "protocol.h" // for FetchFrameHeader() +#include "resman.h" + +extern uint32 sequenceTextLines; // see anims.cpp + +//----------------------------------------------------------------------------- +typedef struct // info for each line of words in the output text sprite +{ + uint16 width; // width of line in pixels + uint16 length; // length of line in characters +} _lineInfo; +//----------------------------------------------------------------------------- +// PROTOTYPES +uint16 AnalyseSentence( uint8 *sentence, uint16 maxWidth, uint32 fontRes, _lineInfo *line ); +mem* BuildTextSprite( uint8 *sentence, uint32 fontRes, uint8 pen, _lineInfo *line, uint16 noOfLines ); +uint16 CharWidth( uint8 ch, uint32 fontRes ); +uint16 CharHeight( uint32 fontRes ); +_frameHeader* FindChar( uint8 ch, uint8 *charSet ); +void CopyChar( _frameHeader *charPtr, uint8 *spritePtr, uint16 spriteWidth, uint8 pen ); +//----------------------------------------------------------------------------- +// global layout variables - these used to be defines, but now we're dealing with 2 character sets (10dec96 JEL) + +int8 line_spacing; // no. of pixels to separate lines of characters in the output sprite - negative for overlap +int8 char_spacing; // no. of pixels to separate characters along each line - negative for overlap +uint8 border_pen; // output pen colour of character borders + +//----------------------------------------------------------------------------- +// Global font resource id variables, set up in 'SetUpFontResources()' at bottom of this file + +uint32 speech_font_id; +uint32 controls_font_id; +uint32 red_font_id; +uint32 death_font_id; + +//----------------------------------------------------------------------------- +mem* MakeTextSprite( uint8 *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes ) +{ + mem *line; // handle for the memory block which will contain the array of lineInfo structures + mem *textSprite; // handle for the block to contain the text sprite itself + uint16 noOfLines; // no of lines of text required to fit within a sprite of width 'maxWidth' pixels + +// Zdebug("MakeTextSprite( \"%s\", maxWidth=%u )", sentence, maxWidth ); + + ///////////////////////////////////////////////////////////////////////////// + // NB. ensure sentence contains no leading/tailing/extra spaces + // - if necessary, copy to another array first, missing the extra spaces. + ///////////////////////////////////////////////////////////////////////////// + + //---------------------------------------------- + // set the global layout variables (10dec96 JEL) + + if (fontRes == speech_font_id) + { + line_spacing = -6; // overlap lines by 6 pixels + char_spacing = -3; // overlap characters by 3 pixels + } + else if (fontRes == CONSOLE_FONT_ID) + { + line_spacing = 0; // no space or overlap between lines + char_spacing = 1; // 1 pixel spacing between each character + } + else + { + line_spacing = 0; + char_spacing = 0; + } + + if (sequenceTextLines) // if rendering text over a sequence + border_pen = 1; // need a different colour number to BORDER_PEN + else + border_pen = BORDER_PEN; + + //---------------------------------------------- + + // allocate memory for array of lineInfo structures + line = Twalloc( MAX_LINES*sizeof(_lineInfo), MEM_locked, UID_temp ); // last param is an optional id for type of mem block + + // get details of sentence breakdown into array of _lineInfo structures + // and get the no of lines involved + noOfLines = AnalyseSentence( sentence, maxWidth, fontRes, (_lineInfo *)line->ad ); + + // construct the sprite based on the info gathered - returns floating mem block + textSprite = BuildTextSprite( sentence, fontRes, pen, (_lineInfo *)line->ad, noOfLines ); + + // free up the lineInfo array now + Free_mem( line ); + + return( textSprite ); +} +//----------------------------------------------------------------------------- +uint16 AnalyseSentence( uint8 *sentence, uint16 maxWidth, uint32 fontRes, _lineInfo *line ) +{ + uint16 pos=0, wordWidth, wordLength, spaceNeeded, firstWord=TRUE, lineNo=0; + uint8 ch; + // joinWidth = how much extra space is needed to append a word to a line + // NB. SPACE requires TWICE the 'char_spacing' to join a word to line + uint16 joinWidth = CharWidth( SPACE, fontRes ) + 2*char_spacing; + + + do + { + wordWidth = 0; // new word + wordLength = 0; + + ch = sentence[pos++]; // get first char of word (at position 'pos') + + while( (ch != SPACE) && ch ) // while not SPACE or NULL terminator + { + // inc wordWidth by (character width + char_spacing) pixels + wordWidth += CharWidth( ch, fontRes ) + char_spacing; + wordLength++; + ch = sentence[pos++]; // get next char + } + + wordWidth -= char_spacing; // no char_spacing after final letter of word! + + // 'ch' is now the SPACE or NULL following the word + // 'pos' indexes to the position following 'ch' + + + if( firstWord ) // first word on first line, so no separating SPACE needed + { + line[0].width = wordWidth; + line[0].length = wordLength; + firstWord = FALSE; + } + else + { + // see how much extra space this word will need to fit on current line + // (with a separating space character - also overlapped) + spaceNeeded = joinWidth + wordWidth; + + if( (line[lineNo].width + spaceNeeded) <= maxWidth ) // fits this line + { + line[lineNo].width += spaceNeeded; + line[lineNo].length += 1+wordLength; // NB. space+word characters + } + else // put word (without separating SPACE) at start of next line + { + lineNo++; // for next _lineInfo structure in the array + //debug_only( lineNo < MAX_LINES ); // exception if lineNo >= MAX_LINES + line[lineNo].width = wordWidth; + line[lineNo].length = wordLength; + } + } + } + while( ch ); // while not reached the NULL terminator + + return lineNo+1; // return no of lines +} + +//----------------------------------------------------------------------------- +// Returns a handle to a floating memory block containing a text sprite, given +// a pointer to a null-terminated string, pointer to required character set, +// required text pen colour (or zero to use source colours), pointer to the +// array of linInfo structures created by 'AnalyseSentence()', and the number +// of lines (ie. no. of elements in the 'line' array). + +// +// +// PC Version of BuildTextSprite +// +// + +mem* BuildTextSprite( uint8 *sentence, uint32 fontRes, uint8 pen, _lineInfo *line, uint16 noOfLines ) +{ + uint8 *linePtr, *spritePtr; + uint16 lineNo, pos=0, posInLine, spriteWidth=0, spriteHeight, sizeOfSprite; + uint16 charHeight = CharHeight(fontRes); + _frameHeader *frameHeadPtr, *charPtr; + mem *textSprite; + uint8 *charSet; + + // spriteWidth = width of widest line of output text + for( lineNo=0; lineNo < noOfLines; lineNo++) + if( line[lineNo].width > spriteWidth ) + spriteWidth = line[lineNo].width; + + // spriteHeight = tot height of char lines + tot height of separating lines + spriteHeight = (charHeight*noOfLines + line_spacing*(noOfLines-1)); + + // total size (no of pixels) + sizeOfSprite = spriteWidth * spriteHeight; + + // allocate memory for sprite, and lock it ready for use + // NB. 'textSprite' is the given pointer to the handle to be used + textSprite = Twalloc( sizeof(_frameHeader) + sizeOfSprite, MEM_locked, UID_text_sprite ); + // the handle (*textSprite) now points to UNMOVABLE memory block + + // set up the frame header + frameHeadPtr = (_frameHeader *)textSprite->ad; // point to the start of our memory block + + frameHeadPtr->compSize = 0; + frameHeadPtr->width = spriteWidth; + frameHeadPtr->height = spriteHeight; + +// Zdebug("spriteWidth=%u",spriteWidth); +// Zdebug("spriteHeight=%u",spriteHeight); + + // ok, now point to the start (of the first line) of the sprite data itelf + linePtr = textSprite->ad + sizeof(_frameHeader); + + // start with transparent sprite (no colour) + memset( linePtr, NO_COL, sizeOfSprite ); + + + charSet = res_man.Res_open(fontRes); // open font file + + + // fill sprite with characters, one line at a time + for( lineNo=0; lineNo < noOfLines; lineNo++ ) + { + // position the start of the line so that it is centred across the sprite + spritePtr = linePtr + (spriteWidth - line[lineNo].width) / 2; + + // copy the sprite for each character in this line to the text sprite + // and inc the sprite ptr by the character's width minus the 'overlap' + for( posInLine=0; posInLine < line[lineNo].length; posInLine++ ) + { + charPtr = FindChar( sentence[pos++], charSet ); + + #ifdef _DEBUG + if ((charPtr->height) != charHeight) + Con_fatal_error("FONT ERROR: '%c' is not same height as the space (%s line %u)",sentence[pos-1],__FILE__,__LINE__); + #endif + + CopyChar( charPtr, spritePtr, spriteWidth, pen ); + spritePtr += charPtr->width + char_spacing; + } + + pos++; // skip space at end of last word in this line + + // move to start of next character line in text sprite + linePtr += (charHeight + line_spacing) * spriteWidth; + } + + + res_man.Res_close(fontRes); // close font file + + + // unlock the sprite memory block, so it's movable + Float_mem( textSprite ); + + return( textSprite ); +} + +//----------------------------------------------------------------------------- +// Returns the width of a character sprite, given the character's ASCII code +// and a pointer to the start of the character set. + +uint16 CharWidth( uint8 ch, uint32 fontRes ) +{ + _frameHeader *charFrame; + uint8 *charSet; + uint16 width; + + + charSet = res_man.Res_open(fontRes); // open font file + + charFrame = FindChar( ch, charSet ); // move to approp. sprite (header) + + width = charFrame->width; + + res_man.Res_close(fontRes); // close font file + + return (width); // return its width +} +//----------------------------------------------------------------------------- +// Returns the height of a character sprite, given the character's ASCII code +// and a pointer to the start of the character set. + +uint16 CharHeight( uint32 fontRes ) // assume all chars the same height! +{ + _frameHeader *charFrame; + uint8 *charSet; + uint16 height; + + + charSet = res_man.Res_open(fontRes); // open font file + + charFrame = FindChar( FIRST_CHAR, charSet ); // FIRST_CHAR as good as any + + height = charFrame->height; + + res_man.Res_close(fontRes); // close font file + + return (height); // return its height +} +//----------------------------------------------------------------------------- +// Returns a pointer to the header of a character sprite, given the character's +// ASCII code and a pointer to the start of the character set. + +_frameHeader* FindChar( uint8 ch, uint8 *charSet ) +{ + // charSet details: + // --------------- + // starts with the standard file header ie. sizeof(_header) bytes + // then an int32 giving the no of sprites ie. 4 bytes + // then the offset table (an int32 offset for each sprite) + // - each offset counting from the start of the file + + if( (ch<FIRST_CHAR) || (ch>LAST_CHAR) ) // if 'ch' out of range + ch = DUD; // then print the 'dud' character (chequered flag) + + // address of char = address of charSet + offset to char + //return (charSet + *(int32 *)(charSet + sizeof(_header) + 4 + 4*(ch - FIRST_CHAR))); + return (FetchFrameHeader( charSet, ch-FIRST_CHAR )); +} +//----------------------------------------------------------------------------- +// Copies a character sprite from 'charPtr' to the sprite buffer at 'spritePtr' +// of width 'spriteWidth'. If pen is zero, it copies the data across directly, +// otherwise it maps pixels of BORDER_COL to 'border_pen', and LETTER_COL to 'pen'. + +void CopyChar( _frameHeader *charPtr, uint8 *spritePtr, uint16 spriteWidth, uint8 pen ) +{ + uint8 *rowPtr, *source, *dest; + uint16 rows, cols; + + + source = (uint8 *)charPtr + sizeof(_frameHeader); // now pts to sprite data for char 'ch' + rowPtr = spritePtr; // pts to start of first row of char within text sprite + + for( rows=0; rows < charPtr->height; rows++ ) + { + dest = rowPtr; // start at beginning of row + + + if (pen) // if required output pen is non-zero + { + for( cols=0; cols < charPtr->width; cols++ ) + { + switch( *source++ ) // inc source ptr along sprite data + { + case LETTER_COL: + *dest = pen; + break; + + case BORDER_COL: + if (!(*dest)) // don't do a border pixel if there already a bit of another character underneath (for overlapping!) + *dest = border_pen; + break; + + // do nothing if source pixel is zero - ie. transparent + } + dest++; // inc dest ptr to next pixel along row + } + } + + else // pen is zero, so just copy character sprites directly into text sprite without remapping colours + { + memcpy( dest, source, charPtr->width ); + source += charPtr->width; + } + + rowPtr += spriteWidth; // next row down (add width of text sprite) + } +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#if _DEBUG +#define MAX_text_blocs MAX_DEBUG_TEXT_BLOCKS+1 // allow enough for all the debug text blocks (see debug.cpp) +#else +#define MAX_text_blocs 2 // only need one for speech, and possibly one for "PAUSED" +#endif // _DEBUG + +typedef struct +{ + int16 x; + int16 y; + uint16 type; // RDSPR_ status bits - see defintion of _spriteInfo structure for correct size! + mem *text_mem; +} text_bloc; + +text_bloc text_sprite_list[MAX_text_blocs]; +//----------------------------------------------------------------------------- +void Init_text_bloc_system(void) //Tony16Oct96 +{ + uint32 j; + + for (j=0;j<MAX_text_blocs;j++) + text_sprite_list[j].text_mem=0; +} +//----------------------------------------------------------------------------- +#define TEXT_MARGIN 12 // distance to keep speech text from edges of screen + +uint32 Build_new_block(uint8 *ascii, int16 x, int16 y, uint16 width, uint8 pen, uint32 type, uint32 fontRes, uint8 justification) //Tony31Oct96 +{ +//creates a text bloc in the list and returns the bloc number +//the list of blocs are read and blitted at render time +//choose alignment type RDSPR_DISPLAYALIGN or 0 + + uint32 j=0; + _frameHeader *frame_head; + int16 text_left_margin; + int16 text_right_margin; + int16 text_top_margin; + int16 text_bottom_margin; + + +//find a free slot + while((j<MAX_text_blocs)&&(text_sprite_list[j].text_mem)) + j++; + +#ifdef _DEBUG + if (j==MAX_text_blocs) //we've run out + Con_fatal_error("Build_new_block ran out of blocks! (%s line %u)",__FILE__,__LINE__); //might as well stop the system +#endif + + + text_sprite_list[j].text_mem = MakeTextSprite( ascii, width, pen, fontRes ); // make the sprite! + + + // speech to be centred above point (x,y), but kept on-screen + // where (x,y) is a point somewhere just above the talker's head + + // debug text just to be printed normally from point (x,y) + + //----------------------------------------------------------- + // JUSTIFICATION & POSITIONING (James updated 20jun97) + + if (justification != NO_JUSTIFICATION) // 'NO_JUSTIFICATION' means print sprite with top-left at (x,y) without margin checking - used for debug text + { + frame_head = (_frameHeader*) text_sprite_list[j].text_mem->ad; + + switch (justification) + { + // this one is always used for SPEECH TEXT; possibly also for pointer text + case POSITION_AT_CENTRE_OF_BASE: + x -= (frame_head->width)/2; // subtract half the sprite-width from the given x-coord + y -= frame_head->height; // and the sprite-height from the given y-coord + break; + + case POSITION_AT_CENTRE_OF_TOP: + x -= (frame_head->width)/2; + break; + + case POSITION_AT_LEFT_OF_TOP: + // the given coords are already correct for this! + break; + + case POSITION_AT_RIGHT_OF_TOP: + x -= frame_head->width; + break; + + case POSITION_AT_LEFT_OF_BASE: + y -= frame_head->height; + break; + + case POSITION_AT_RIGHT_OF_BASE: + x -= frame_head->width; + y -= frame_head->height; + break; + + case POSITION_AT_LEFT_OF_CENTRE: + y -= (frame_head->height)/2; + break; + + case POSITION_AT_RIGHT_OF_CENTRE: + x -= frame_head->width; + y -= (frame_head->height)/2; + break; + } + + // ensure text sprite is a few pixels inside the visible screen + text_left_margin = TEXT_MARGIN; + text_right_margin = 640 - TEXT_MARGIN - frame_head->width; + text_top_margin = 0 + TEXT_MARGIN; // remember - it's RDSPR_DISPLAYALIGN + text_bottom_margin = 400 - TEXT_MARGIN - frame_head->height; + + if (x < text_left_margin) // move if too far left or too far right + x = text_left_margin; + else if (x > text_right_margin) + x = text_right_margin; + + if (y < text_top_margin) // move if too high or too low + y = text_top_margin; + else if (y > text_bottom_margin) + y = text_bottom_margin; + } + //----------------------------------------------------------- + + text_sprite_list[j].x = x; + text_sprite_list[j].y = y; + text_sprite_list[j].type = type+RDSPR_NOCOMPRESSION; // always uncompressed + + + return(j+1); +} +//----------------------------------------------------------------------------- + +// +// +// PC Version of Print_text_blocs +// +// + +void Print_text_blocs(void) //Tony16Oct96 +{ +//called by build_display + + _frameHeader *frame; + _spriteInfo spriteInfo; + uint32 j; + uint32 rv; + + for (j=0;j<MAX_text_blocs;j++) + { + if (text_sprite_list[j].text_mem) + { + frame = (_frameHeader*) text_sprite_list[j].text_mem->ad; + + spriteInfo.x = text_sprite_list[j].x; + spriteInfo.y = text_sprite_list[j].y; + spriteInfo.w = frame->width; + spriteInfo.h = frame->height; + spriteInfo.scale = 0; + spriteInfo.scaledWidth = 0; + spriteInfo.scaledHeight = 0; + spriteInfo.type = text_sprite_list[j].type; + spriteInfo.blend = 0; + spriteInfo.data = text_sprite_list[j].text_mem->ad+sizeof(_frameHeader); + spriteInfo.colourTable = 0; + + rv = DrawSprite( &spriteInfo ); + if (rv) + ExitWithReport("Driver Error %.8x in Print_text_blocs [%s line %u]", rv, __FILE__, __LINE__); + } + } +} + +//----------------------------------------------------------------------------- +void Kill_text_bloc(uint32 bloc_number) //Tony18Oct96 +{ + bloc_number--; //back to real + + if (text_sprite_list[bloc_number].text_mem) + { + Free_mem(text_sprite_list[bloc_number].text_mem); //release the floating memory + text_sprite_list[bloc_number].text_mem=0; //this is how we know the bloc is free + } + else + Con_fatal_error("closing closed text bloc number %d", bloc_number); //illegal kill - stop the system + +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// called from InitialiseGame() in sword2.cpp +void InitialiseFontResourceFlags(void) // (James31july97) +{ + uint8 *textFile, *textLine; + uint8 language; + + #define TEXT_RES 3258 // resource 3258 contains text from location script for 152 (install, save & restore text, etc) + #define SAVE_LINE_NO 1 // local line number of "save" (actor no. 1826) + +#ifndef _DEMO // normal game + #define NAME_LINE_NO 54 // local line number of game name (actor no. 3550) +#else + #define NAME_LINE_NO 451 // local line number of demo game name +#endif // _DEMO + + //--------------------------------------------------------------------------------- + textFile = res_man.Res_open(TEXT_RES); // open the text resource + //--------------------------------------------------------------------------------- + // check if language is Polish or Finnish, and therefore requires alternate fonts + + textLine = FetchTextLine(textFile, SAVE_LINE_NO )+2; // get the text line (& skip the 2 chars containing the wavId) + + if (strcmp((char*)textLine,"tallenna")==0) // if this line contains the Finnish for "save" + language = FINNISH_TEXT; // - then this version must be Finnish + else if (strcmp((char*)textLine,"zapisz")==0) // if this line contains the Polish for "save" + language = POLISH_TEXT; // - then this version must be Polish + else // neither Finnish nor Polish + language = DEFAULT_TEXT; // - use regular fonts + + InitialiseFontResourceFlags(language); // Set the game to use the appropriate fonts + + //--------------------------------------------------------------------------------- + // Get the game name for the windows application + + textLine = FetchTextLine(textFile, NAME_LINE_NO )+2; // get the text line (& skip the 2 chars containing the wavId) + SetWindowName((char*)textLine); // driver function + //--------------------------------------------------------------------------------- + res_man.Res_close(TEXT_RES); // now ok to close the text file + //--------------------------------------------------------------------------------- +} +//------------------------------------------------------------------------------------ +// called from the above function, and also from console.cpp +void InitialiseFontResourceFlags(uint8 language) // (James31july97) +{ + switch (language) + { + case FINNISH_TEXT: // special Finnish fonts + { + speech_font_id = FINNISH_SPEECH_FONT_ID; + controls_font_id = FINNISH_CONTROLS_FONT_ID; + red_font_id = FINNISH_RED_FONT_ID; + break; + } + + case POLISH_TEXT: // special Polish fonts + { + speech_font_id = POLISH_SPEECH_FONT_ID; + controls_font_id = POLISH_CONTROLS_FONT_ID; + red_font_id = POLISH_RED_FONT_ID; + break; + } + + default:// DEFAULT_TEXT // regular fonts + { + speech_font_id = ENGLISH_SPEECH_FONT_ID; + controls_font_id = ENGLISH_CONTROLS_FONT_ID; + red_font_id = ENGLISH_RED_FONT_ID; + break; + } + } +} +//------------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- + + + + + + diff --git a/sword2/maketext.h b/sword2/maketext.h new file mode 100644 index 0000000000..e770687361 --- /dev/null +++ b/sword2/maketext.h @@ -0,0 +1,94 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +/**************************************************************************** + * MAKETEXT.H Function prototype for text sprite builder routine JEL Oct96 + * + * The routine returns a memory handle to a movable memory block containing + * the required sprite, which must be locked before use. + * ie. lock, draw sprite, unlock/free. + * The sprite data contains a frameHeader, but not a standard file header. + * + * Debugger will trap error when word too big for line (maxWidth) + * or when more lines needed than max expected (MAX_LINES) + * + * PARAMETERS: + * + * 'sentence' points to a NULL-TERMINATED STRING + * - string must contain no leading/tailing/extra spaces + * - out-of-range characters in the string are forced to the output as a + * special error-signal character (chequered flag) + * + * 'maxWidth' is the maximum allowed text sprite width, in PIXELS + * + * 'pen' is the desired colour (0-255) for the main body of each character + * NB. Border colour is #DEFINEd in textsprt.c (to a colour value for BLACK) + * if 'pen' is zero, the characters are copied directly and NOT remapped. + * + * 'charSet' points to the beginning of the standard file header for the + * desired character set + * NB. The first and last characters in the set are #DEFINEd in textsprt.c + * + * + * RETURNS: + * + * 'textSprite' points to the handle to be used for the text sprite + * + ****************************************************************************/ + +#ifndef _MAKETEXT_H +#define _MAKETEXT_H + +//#include "src\driver96.h" +#include "memory.h" + +#define NO_JUSTIFICATION 0 // only for debug text, since it doesn't keep text inside the screen margin! +#define POSITION_AT_CENTRE_OF_BASE 1 // these all force text inside the screen edge margin when necessary +#define POSITION_AT_CENTRE_OF_TOP 2 +#define POSITION_AT_LEFT_OF_TOP 3 +#define POSITION_AT_RIGHT_OF_TOP 4 +#define POSITION_AT_LEFT_OF_BASE 5 +#define POSITION_AT_RIGHT_OF_BASE 6 +#define POSITION_AT_LEFT_OF_CENTRE 7 +#define POSITION_AT_RIGHT_OF_CENTRE 8 + +mem* MakeTextSprite( uint8 *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes ); +void Init_text_bloc_system(void); + +void Kill_text_bloc(uint32 bloc_number); +void Print_text_blocs(void); //Tony16Oct96 + +uint32 Build_new_block(uint8 *ascii, int16 x, int16 y, uint16 width, uint8 pen, uint32 type, uint32 fontRes, uint8 justification); + +//----------------------------------------------------------------------------- + +#define DEFAULT_TEXT 0 +#define FINNISH_TEXT 1 +#define POLISH_TEXT 2 + +void InitialiseFontResourceFlags(void); // this one works out the language from the text cluster (James31july97) +void InitialiseFontResourceFlags(uint8 language); // this one allow you to select the fonts yourself (James31july97) + +extern uint32 speech_font_id; +extern uint32 controls_font_id; +extern uint32 red_font_id; + +//----------------------------------------------------------------------------- + +#endif diff --git a/sword2/mem_view.cpp b/sword2/mem_view.cpp new file mode 100644 index 0000000000..11542ce576 --- /dev/null +++ b/sword2/mem_view.cpp @@ -0,0 +1,259 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//-------------------------------------------------------------------------------------- +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +//#include <windows.h> + +#include "driver/driver96.h" +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "layers.h" +#include "mem_view.h" +#include "memory.h" +#include "resman.h" +#include "sword2.h" // (James11aug97) for CloseGame() + +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- + +char buf[50]; //has to be global because a local in Fetch_mem_owner is destroyed on exit +//-------------------------------------------------------------------------------------- +void Console_mem_display(void) //Tony13Aug96 +{ +// + int pass,found_end,k,j,free=0; + _standardHeader *file_header; + int scrolls=0; + char c; + + char inf[][20]= + { + {"M_null "}, + {"M_free "}, + {"M_locked"}, + {"M_float "} + }; + + + j=base_mem_block; + do + { + + if (mem_list[j].uid<65536) + { + file_header = (_standardHeader*) res_man.Res_open(mem_list[j].uid); + res_man.Res_close(mem_list[j].uid); //close immediately so give a true count + + Zdebug("view %d", mem_list[j].uid); + + + pass=0; + found_end=0; + + for (k=0;k<30;k++) + { + if (file_header->name[k]==0) + { found_end=1; + break; + } + + if ( (file_header->name[k]<32)||(file_header->name[k]>'z')) + pass=1; + + } + + if (file_header->name[0]==0) + pass=1; //also illegal + + + if ((!pass)&&(found_end)) //&&(file_header->fileType<10)) + Print_to_console("%d %s, size 0x%.5x (%dk %d%%), res %d %s %s, A%d, C%d", j, + inf[mem_list[j].state], + mem_list[j].size, mem_list[j].size/1024, (mem_list[j].size*100)/total_free_memory, mem_list[j].uid, + + res_man.Fetch_cluster(mem_list[j].uid), + file_header->name, + res_man.Fetch_age(mem_list[j].uid), + res_man.Fetch_count(mem_list[j].uid)); + + else Print_to_console(" %d is an illegal resource", mem_list[j].uid); + + } + else + Print_to_console("%d %s, size 0x%.5x (%dk %d%%), %s", j, + inf[mem_list[j].state], + mem_list[j].size, mem_list[j].size/1024, (mem_list[j].size*100)/total_free_memory, + Fetch_mem_owner(mem_list[j].uid) ); + + if (mem_list[j].state==MEM_free) + free+=mem_list[j].size; + + + j=mem_list[j].child; + + scrolls++; + + Build_display(); + + + if (scrolls==18) + { + Temp_print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(!KeyWaiting()); + + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + + Clear_console_line(); //clear the Press Esc message ready for the new line + scrolls=0; + } + } + while (j!=-1); + + Scroll_console(); + Print_to_console("(total memory block 0x%.8x %dk %dMB) %d / %d%% free", total_free_memory, + total_free_memory/1024, + total_free_memory/(1000*1024), + free, + (free*100)/total_free_memory); + + +} +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +char *Fetch_mem_owner(uint32 uid) //Tony3June96 +{ + + switch(uid) + { + case UID_memman: + return("MEMMAN"); + break; + + case UID_font: + return("font"); + break; + + case UID_temp: + return("temp ram allocation"); + break; + + case UID_decompression_buffer: + return("decompression buffer"); + break; + + case UID_shrink_buffer: + return("shrink buffer"); + break; + + case UID_con_sprite: + return("console sprite buffer"); + break; + + case UID_text_sprite: + return("text sprite"); + break; + + case UID_walk_anim: + return("walk anim"); + break; + + case UID_savegame_buffer: + return("savegame buffer"); + break; + + default: + sprintf(buf, "<sob> %d?", uid); + return(buf); + break; + } +} + +//-------------------------------------------------------------------------------------- +void Create_mem_string( char *string ) // James (21oct96 updated 4dec96) +{ + int blockNo = base_mem_block; + int blocksUsed=0; + int mem_free=0; + int mem_locked=0; + int mem_floating=0; + int memUsed=0; + int percent; + + + + while (blockNo != -1) + { + switch (mem_list[blockNo].state) + { + case MEM_free: + mem_free++; + break; + + case MEM_locked: + mem_locked++; + memUsed += mem_list[blockNo].size; + break; + + case MEM_float: + mem_floating++; + memUsed += mem_list[blockNo].size; + break; + } + + blocksUsed++; + blockNo = mem_list[blockNo].child; + } + + percent = (memUsed * 100) / total_free_memory; + + sprintf( string, "locked(%u)+float(%u)+free(%u) = %u/%u blocks (%u%% used)(cur %uk)", mem_locked, mem_floating, mem_free, blocksUsed, MAX_mem_blocks, percent, (res_man.Res_fetch_useage()/1024) ); +} +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- diff --git a/sword2/mem_view.h b/sword2/mem_view.h new file mode 100644 index 0000000000..725c55ff5c --- /dev/null +++ b/sword2/mem_view.h @@ -0,0 +1,31 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef MEMVIEW_H +#define MEMVIEW_H + +//#include "src\driver96.h" + + +char *Fetch_mem_owner(uint32 uid); +void Console_mem_display(void); // Tony (13Aug96) +void Create_mem_string( char *string ); // James (21oct96 updated 4dec96) + + +#endif diff --git a/sword2/memory.cpp b/sword2/memory.cpp new file mode 100644 index 0000000000..a8fba5b780 --- /dev/null +++ b/sword2/memory.cpp @@ -0,0 +1,544 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//memory manager - "remember, it's not good to leave memory locked for a moment longer than necessary" Tony +// "actually, in a sequential system theoretically you never need to lock any memory!" Chris ;) +// +// This is a very simple implementation but I see little advantage to being any cleverer +// with the coding - i could have put the mem blocks before the defined blocks instead +// of in an array and then used pointers to child/parent blocks. But why bother? I've Kept it simple. +// When it needs updating or customising it will be accessable to anyone who looks at it. +// *doesn't have a purgeable/age consituant yet - if anyone wants this then I'll add it in. + + +// MemMan v1.1 + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "driver/driver96.h" +#include "console.h" +#include "debug.h" +#include "memory.h" +#include "resman.h" + + +uint32 total_blocks; +uint32 base_mem_block; +uint32 total_free_memory; +uint8 *free_memman; //address of init malloc to be freed later + +//#define MEMDEBUG 1 + +mem mem_list[MAX_mem_blocks]; //list of defined memory handles - each representing a block of memory. + +int32 VirtualDefrag( uint32 size ); // Used to determine if the required size can be obtained if the defragger is allowed to run. +int32 suggestedStart = 0; // Start position of the Defragger as indicated by its sister VirtualDefrag. + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void Close_memory_manager(void) //Tony2Oct96 +{ + +//unlock our supposedly locked in memory + VirtualUnlock(free_memman, total_free_memory); + + free(free_memman); +} +//------------------------------------------------------------------------------------ +void Init_memory_manager(void) //Tony9April96 +{ + uint32 j; + uint8 *memory_base; + //BOOL res; + MEMORYSTATUS memo; + +//find out how much actual physical RAM this computer has + GlobalMemoryStatus(&memo); + +//now decide how much to grab - 8MB computer are super critical + if (memo.dwTotalPhys<=(8000*1024)) //if 8MB or less :-O + total_free_memory=4500*1024; //4.5MB + + else if (memo.dwTotalPhys<=(12000*1024)) //if 8MB or less :-O + total_free_memory=8000*1024; //8MB + + else if (memo.dwTotalPhys<=(16000*1024)) //if 16MB or less :-) + total_free_memory=10000*1024; //10MB + + else //:-)) loads of RAM + total_free_memory=12000*1024; //12MB + + + + Zdebug("MEM = %d", memo.dwTotalPhys); + Zdebug("Sword 2 grabbed %dk", total_free_memory/1024); + + + +//malloc memory and adjust for long boundaries + memory_base = (uint8 *) malloc(total_free_memory); + + if (!memory_base) //could not grab the memory + { + Zdebug("couldn't malloc %d in Init_memory_manager", total_free_memory); + ExitWithReport("Init_memory_manager() couldn't malloc %d bytes [line=%d file=%s]",total_free_memory,__LINE__,__FILE__); + } + + free_memman = memory_base; //the original malloc address + +//force to long word boundary + memory_base+=3; + memory_base = (uint8 *)((uint32)memory_base & 0xfffffffc); // ** was (int)memory_base +// total_free_memory-=3; //play safe + + + +//set all but first handle to unused + for (j=1;j<MAX_mem_blocks;j++) + mem_list[j].state=MEM_null; + + + total_blocks=1; //total used (free, locked or floating) + + mem_list[0].ad = memory_base; + mem_list[0].state= MEM_free; + mem_list[0].age=0; + mem_list[0].size=total_free_memory; + mem_list[0].parent=-1; //we are base - for now + mem_list[0].child=-1; //we are the end as well + mem_list[0].uid=UID_memman; //init id + + base_mem_block=0; //for now + + +//supposedly this will stop the memory swapping out?? Well, as much as we're allowed +// res=VirtualLock(free_memman, total_free_memory); + +// if (res!=TRUE) +// Zdebug(" *VirtualLock failed"); +} +//------------------------------------------------------------------------------------ +mem *Talloc(uint32 size, uint32 type, uint32 unique_id) //Tony10Apr96 +{ +//allocate a block of memory - locked or float + +// returns 0 if fails to allocate the memory +// or a pointer to a mem structure + + int32 nu_block; + uint32 spawn=0; + uint32 slack; + + + + + +//we must first round the size UP to a dword, so subsequent blocks will start dword alligned + size+=3; //move up + size &= 0xfffffffc; //and back down to boundary + + + + +//find a free block large enough + if ( (nu_block = Defrag_mem(size))==-1) //the defragger returns when its made a big enough block. This is a good time to defrag as we're probably not + { //doing anything super time-critical at the moment + return(0); //error - couldn't find a big enough space + } + + + +//an exact fit? + if (mem_list[nu_block].size==size) //no new block is required as the fit is perfect + { + mem_list[nu_block].state=type; //locked or float + mem_list[nu_block].size=size; //set to the required size + mem_list[nu_block].uid=unique_id; //an identifier + +#ifdef MEMDEBUG + Mem_debug(); +#endif //MEMDEBUG + return(&mem_list[nu_block]); + } + + +// nu_block is the free block to split, forming our locked/float block with a new free block in any remaining space + + +//if our child is free then is can expand downwards to eat up our chopped space +//this is good because it doesn't create an extra bloc so keeping the block count down +//why? +//imagine you Talloc 1000k, then free it. Now keep allocating 10 bytes less and freeing again +//you end up with thousands of new free mini blocks. this way avoids that as the free child keeps growing downwards + if ((mem_list[nu_block].child != -1) && (mem_list[mem_list[nu_block].child].state==MEM_free)) //our child is free + { + slack=mem_list[nu_block].size-size; //the spare memory is the blocks current size minus the amount we're taking + + mem_list[nu_block].state=type; //locked or float + mem_list[nu_block].size=size; //set to the required size + mem_list[nu_block].uid=unique_id; //an identifier + + mem_list[mem_list[nu_block].child].ad = mem_list[nu_block].ad+size; //child starts after us + mem_list[mem_list[nu_block].child].size += slack; //childs size increases + + return(&mem_list[nu_block]); + } + + +// otherwise we spawn a new block after us and before our child - our child being a proper block that we cannot change + +// we remain a child of our parent +// we spawn a new child and it inherits our current child + +//find a NULL slot for a new block + while((mem_list[spawn].state!=MEM_null)&&(spawn!=MAX_mem_blocks)) + spawn++; + + + if (spawn==MAX_mem_blocks) //run out of blocks - stop the program. this is a major blow up and we need to alert the developer + { + Mem_debug(); //Lets get a printout of this + ExitWithReport("ERROR: ran out of mem blocks in Talloc() [file=%s line=%u]",__FILE__,__LINE__); + } + + + + mem_list[spawn].state=MEM_free; //new block is free + mem_list[spawn].uid=UID_memman; //a memman created bloc + mem_list[spawn].size= mem_list[nu_block].size-size; //size of the existing parent free block minus the size of the new space Talloc'ed. + //IOW the remaining memory is given to the new free block + mem_list[spawn].ad = mem_list[nu_block].ad+size; //we start 1 byte after the newly allocated block + mem_list[spawn].parent=nu_block; //the spawned child gets it parent - the newly allocated block + + mem_list[spawn].child=mem_list[nu_block].child; //the new child inherits the parents old child (we are its new child "Waaaa") + + + + if (mem_list[spawn].child!=-1) //is the spawn the end block? + mem_list[mem_list[spawn].child].parent= spawn; //the child of the new free-spawn needs to know its new parent + + + mem_list[nu_block].state=type; //locked or float + mem_list[nu_block].size=size; //set to the required size + mem_list[nu_block].uid=unique_id; //an identifier + mem_list[nu_block].child=spawn; //the new blocks new child is the newly formed free block + + + total_blocks++; //we've brought a new block into the world. Ahhh! + + +#ifdef MEMDEBUG + Mem_debug(); +#endif //MEMDEBUG + + return(&mem_list[nu_block]); +} +//------------------------------------------------------------------------------------ +void Free_mem(mem *block) //Tony10Apr96 +{ +//kill a block of memory - which was presumably floating or locked +//once you've done this the memory may be recycled + + block->state=MEM_free; + block->uid=UID_memman; //belongs to the memory manager again + +#ifdef MEMDEBUG + Mem_debug(); +#endif //MEMDEBUG +} +//------------------------------------------------------------------------------------ +void Float_mem(mem *block) //Tony10Apr96 +{ +//set a block to float +//wont be trashed but will move around in memory + + block->state=MEM_float; + +#ifdef MEMDEBUG + Mem_debug(); +#endif //MEMDEBUG +} +//------------------------------------------------------------------------------------ +void Lock_mem(mem *block) //Tony11Apr96 +{ +//set a block to lock +//wont be moved - don't lock memory for any longer than necessary unless you know the locked memory is at the bottom of the heap + + block->state=MEM_locked; //can't move now - this block is now crying out to be floated or free'd again + +#ifdef MEMDEBUG + Mem_debug(); +#endif //MEMDEBUG +} +//------------------------------------------------------------------------------------ +int32 Defrag_mem(uint32 req_size) //Tony10Apr96 +{ +//moves floating blocks down and/or merges free blocks until a large enough space is found +//or there is nothing left to do and a big enough block cannot be found +//we stop when we find/create a large enough block - this is enough defragging. + + int32 cur_block; //block 0 remains the parent block + int32 original_parent,child, end_child; + uint32 j; + uint32 *a; + uint32 *b; + + +// cur_block=base_mem_block; //the mother of all parents + cur_block = suggestedStart; + + + do + { + if (mem_list[cur_block].state==MEM_free) //is current block a free block? + { + + if (mem_list[cur_block].size>=req_size) + { + return(cur_block); //this block is big enough - return its id + } + + + if (mem_list[cur_block].child==-1) //the child is the end block - stop if the next block along is the end block + return(-1); //no luck, couldn't find a big enough block + + +// current free block is too small, but if its child is *also* free then merge the two together + if (mem_list[mem_list[cur_block].child].state==MEM_free) + { +// ok, we nuke the child and inherit its child + + child=mem_list[cur_block].child; + + mem_list[cur_block].size+= mem_list[child].size; //our size grows by the size of our child + mem_list[cur_block].child = mem_list[child].child; //our new child is our old childs, child + + if (mem_list[child].child!=-1) //not if the chld we're nuking is the end child (it has no child) + mem_list[mem_list[child].child].parent=cur_block; //the (nuked) old childs childs parent is now us + + mem_list[child].state=MEM_null; //clean up the nuked child, so it can be used again + + total_blocks--; + } + + +// current free block is too small, but if its child is a float then we move the floating memory block down and the free up +// but, parent/child relationships must be such that the memory is all continuous between blocks. ie. a childs memory always +// begins 1 byte after its parent finishes. However, the positions in the memory list may become truly random, but, any particular +// block of locked or floating memory must retain its position within the mem_list - the float stays a float because the handle/pointer has been passed back +// what this means is that when the physical memory of the foat moves down (and the free up) the child becomes the parent and the parent the child +// but, remember, the parent had a parent and the child another child - these swap over too as the parent/child swap takes place - phew. + else if (mem_list[mem_list[cur_block].child].state==MEM_float) + { + child=mem_list[cur_block].child; //our child is currently floating + + // memcpy(mem_list[cur_block].ad, mem_list[child].ad, mem_list[child].size); //move the higher float down over the free block + + + a=(uint32*) mem_list[cur_block].ad; + b=(uint32*) mem_list[child].ad; + + for (j=0;j<mem_list[child].size/4;j++) + *(a++)=*(b++); + + +// both *ad's change + mem_list[child].ad = mem_list[cur_block].ad; //the float is now where the free was + mem_list[cur_block].ad += mem_list[child].size; //and the free goes up by the size of the float (which has come down) + +// the status of the mem_list blocks must remain the same, so... + original_parent= mem_list[cur_block].parent; //our child gets this when we become its child and it our parent + mem_list[cur_block].parent=child; //the free's child becomes its parent + mem_list[cur_block].child= mem_list[child].child; //the new child inherits its previous childs child + + end_child=mem_list[child].child; //save this - see next line + + mem_list[child].child=cur_block; //the floats parent becomes its child + mem_list[child].parent= original_parent; + + if (end_child!=-1) //if the child had a child + mem_list[end_child].parent=cur_block; //then its parent is now the new child + + if (original_parent==-1) //the base block was the true base parent + base_mem_block=child; //then the child that has moved down becomes the base block as it sits at the lowest possible memory location + else + mem_list[original_parent].child=child; //otherwise the parent of the current free block - that is now the child - gets a new child, + //that child being previously the child of the child of the original parent + } + else //if (mem_list[mem_list[cur_block].child].state==MEM_lock) //the child of current is locked - move to it + cur_block=mem_list[cur_block].child; //move to next one along - either locked or END + + } + else + { + cur_block=mem_list[cur_block].child; //move to next one along, the current must be floating, locked, or a NULL slot + } + + } + while(cur_block!=-1); //while the block we've just done is not the final block + + return(-1); //no luck, couldn't find a big enough block +} +//------------------------------------------------------------------------------------ +void Mem_debug(void) //Tony11Apr96 +{ +//gets called with Talloc, Mem_free, Mem_lock & Mem_float if MEMDEBUG has been #defined +//otherwise can be called at any time anywhere else + + int j; + char inf[][20]= + { + {"MEM_null"}, + {"MEM_free"}, + {"MEM_locked"}, + {"MEM_float"} + }; + + Zdebug("\nbase %d total %d", base_mem_block, total_blocks); + + +//first in mem list order + for (j=0;j<MAX_mem_blocks;j++) + { + if (mem_list[j].state==MEM_null) + Zdebug("%d- NULL", j); + else + Zdebug("%d- state %s, ad %d, size %d, p %d, c %d, id %d", j, + inf[mem_list[j].state], + mem_list[j].ad, mem_list[j].size, mem_list[j].parent, mem_list[j].child, mem_list[j].uid); + } + + +//now in child/parent order + j=base_mem_block; + do + { + Zdebug(" %d- state %s, ad %d, size %d, p %d, c %d", j, + inf[mem_list[j].state], + mem_list[j].ad, mem_list[j].size, mem_list[j].parent, mem_list[j].child, mem_list[j].uid); + + j=mem_list[j].child; + } + while (j!=-1); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +mem *Twalloc(uint32 size, uint32 type, uint32 unique_id) //tony12Feb97 +{ +//the high level Talloc +//can ask the resman to remove old resources to make space - will either do it or halt the system + + mem *membloc; + int j; + uint32 free=0; + + while( VirtualDefrag(size) ) + { + if (!res_man.Help_the_aged_out()) //trash the oldest closed resource + { + Zdebug("Twalloc ran out of memory! %d %d %d\n", size, type, unique_id); + ExitWithReport("Twalloc ran out of memory!"); + } + } + + membloc = Talloc(size, type, unique_id); + + if (membloc == 0) + { + Zdebug("Talloc failed to get memory VirtualDefrag said was there"); + ExitWithReport("Talloc failed to get memory VirtualDefrag said was there"); + } + + j=base_mem_block; + do + { + + if (mem_list[j].state==MEM_free) + free+=mem_list[j].size; + + j=mem_list[j].child; + } + while (j!=-1); + + return(membloc); //return the pointer to the memory +} + + +#define MAX_WASTAGE 51200 // Maximum allowed wasted memory. + +int32 VirtualDefrag( uint32 size ) // Chris - 07 April '97 +{ + // + // Virutually defrags memory... + // + // Used to determine if there is potentially are large enough free block available is the + // real defragger was allowed to run. + // + // The idea being that Twalloc will call this and help_the_aged_out until we indicate that + // it is possible to obtain a large enough free block. This way the defragger need only + // run once to yield the required block size. + // + // The reason for its current slowness is that the defragger is potentially called several + // times, each time shifting upto 20Megs around, to obtain the required free block. + // + int32 cur_block; + uint32 currentBubbleSize = 0; + + cur_block=base_mem_block; + suggestedStart = base_mem_block; + + do + { + if (mem_list[cur_block].state == MEM_free) + { + // Add a little intelligence. At the start the oldest resources are at the bottom of the + // tube. However there will be some air at the top. Thus bubbles will be + // created at the bottom and float to the top. If we ignore the top gap + // then a large enough bubble will form lower down the tube. Thus less memory + // will need to be shifted. + + if (mem_list[cur_block].child != -1) + currentBubbleSize += mem_list[cur_block].size; + else if (mem_list[cur_block].size > MAX_WASTAGE) + currentBubbleSize += mem_list[cur_block].size; + + if (currentBubbleSize >= size) + return 0; + } + else if (mem_list[cur_block].state == MEM_locked) + { + currentBubbleSize = 0; + suggestedStart = mem_list[cur_block].child; // Any free block of the correct size will be above this locked block. + } + + cur_block = mem_list[cur_block].child; + } + while(cur_block != -1); + + return(1); +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ diff --git a/sword2/memory.h b/sword2/memory.h new file mode 100644 index 0000000000..0ea75a47dc --- /dev/null +++ b/sword2/memory.h @@ -0,0 +1,82 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef MEMORY_H +#define MEMORY_H + +#include "common/scummsys.h" +//#include "src\driver96.h" + + +typedef struct +{ + uint32 state; + uint32 age; // *not used* + uint32 size; + int32 parent; //who is before us + int32 child; //who is after us + uint32 uid; //id of a position in the resList or some other unique id - for the visual display only + uint8 *ad; +} mem; + + +#define MEM_null 0 //null +#define MEM_free 1 +#define MEM_locked 2 +#define MEM_float 3 + +//--------------------------------------- +// MEMORY BLOCKS + +#define MAX_mem_blocks 999 + +// maintain at a good 50% higher than the +// highest recorded value from the on-screen info +//--------------------------------------- + +#define UID_memman 0xffffffff +#define UID_NULL 0xfffffffe //FREE +#define UID_font 0xfffffffd +#define UID_temp 0xfffffffc +#define UID_decompression_buffer 0xfffffffb +#define UID_shrink_buffer 0xfffffffa +#define UID_con_sprite 0xfffffff9 +#define UID_text_sprite 0xfffffff8 +#define UID_walk_anim 0xfffffff7 +#define UID_savegame_buffer 0xfffffff6 +#define UID_restoregame_buffer 0xfffffff5 + +void Init_memory_manager(void); +void Close_memory_manager(void); //Tony2Oct96 +//mem *Talloc(uint32 size, uint32 type, uint32 unique_id); //low level +mem *Twalloc(uint32 size, uint32 type, uint32 unique_id); //high level +void Free_mem(mem *block); +void Float_mem(mem *block); +void Lock_mem(mem *block); +void Mem_debug(void); +void Visual_mem_display(void); +int32 Defrag_mem(uint32 req_size); //Tony10Apr96 + + +extern uint32 total_blocks; +extern uint32 base_mem_block; +extern mem mem_list[MAX_mem_blocks]; +extern uint32 total_free_memory; + +#endif diff --git a/sword2/module.mk b/sword2/module.mk new file mode 100644 index 0000000000..b5892f6204 --- /dev/null +++ b/sword2/module.mk @@ -0,0 +1,46 @@ +MODULE := bs2 + +MODULE_OBJS = \ + bs2/anims.o \ + bs2/build_display.o \ + bs2/console.o \ + bs2/controls.o \ + bs2/debug.o \ + bs2/events.o \ + bs2/function.o \ + bs2/icons.o \ + bs2/interpreter.o \ + bs2/layers.o \ + bs2/logic.o \ + bs2/maketext.o \ + bs2/memory.o \ + bs2/mem_view.o \ + bs2/mouse.o \ + bs2/protocol.o \ + bs2/resman.o \ + bs2/router.o \ + bs2/save_rest.o \ + bs2/scroll.o \ + bs2/sound.o \ + bs2/speech.o \ + bs2/startup.o \ + bs2/sword2.o \ + bs2/sync.o \ + bs2/tony_gsdk.o \ + bs2/walker.o \ + bs2/driver/_console.o \ + bs2/driver/d_draw.o \ + bs2/driver/d_sound.o \ + bs2/driver/keyboard.o \ + bs2/driver/language.o \ + bs2/driver/menu.o \ + bs2/driver/misc.o \ + bs2/driver/_mouse.o \ + bs2/driver/palette.o \ + bs2/driver/rdwin.o \ + bs2/driver/render.o \ + bs2/driver/sprite.o + + +# Include common rules +include common.rules diff --git a/sword2/mouse.cpp b/sword2/mouse.cpp new file mode 100644 index 0000000000..7332b5b39b --- /dev/null +++ b/sword2/mouse.cpp @@ -0,0 +1,1466 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include "driver/driver96.h" +#include "build_display.h" +#include "console.h" +#include "controls.h" +#include "debug.h" +#include "defs.h" +#include "events.h" +#include "icons.h" +#include "interpreter.h" +#include "layers.h" +#include "maketext.h" +#include "mouse.h" //assure integrety +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "sound.h" +#include "sword2.h" // for PauseGame() & UnpauseGame() +//------------------------------------------------------------------------------------ +// pointer resource id's + +#define CROSHAIR 18 +#define EXIT0 788 +#define EXIT1 789 +#define EXIT2 790 +#define EXIT3 791 +#define EXIT4 792 +#define EXIT5 793 +#define EXIT6 794 +#define EXIT7 795 +#define EXITDOWN 796 +#define EXITUP 797 +#define MOUTH 787 +#define NORMAL 17 +#define PICKUP 3099 +#define SCROLL_L 1440 +#define SCROLL_R 1441 +#define USE 3100 +//------------------------------------------------------------------------------------ +//the mouse list stuff +uint32 cur_mouse; +Mouse_unit mouse_list[TOTAL_mouse_list]; + +uint32 mouse_touching=0; //set by Check_mouse_list +uint32 old_mouse_touching=0; + +uint32 menu_selected_pos; +uint8 examining_menu_icon=0; + +uint32 mouse_pointer_res=0; // if it's NORMAL_MOUSE_ID (ie. normal pointer) then it's over a floor area (or hidden hot-zone +uint32 mouse_mode=0; //0 normal in game + //1 top menu down (bottom!) + //2 dragging luggage + //3 system menu chooser (top) + //4 speech chooser + +//Object_mouse old_mouse_object; //copy structure from list when moving onto a mouse area + +uint32 menu_status; //0 available - 1 unavailable +uint32 mouse_status; //human 0 on/1 off + +uint32 mouse_mode_locked=0; //0 not !0 mode cannot be changed from normal mouse to top menu (i.e. when carrying big objects) +uint32 current_luggage_resource=0; + +uint32 subject_status; //0 off 1 on + +uint32 old_button=0; //for the re-click stuff - must be same button you see +uint32 button_click=0; + +uint32 pointer_text_bloc_no=0; +uint32 pointerTextSelected=0; + +uint32 player_activity_delay=0; // player activity delay counter + +uint32 real_luggage_item=0; //last minute for pause mode + +//------------------------------------------------------------------------------------ +/* +#define RD_LEFTBUTTONDOWN 0x01 +#define RD_LEFTBUTTONUP 0x02 +#define RD_RIGHTBUTTONDOWN 0x04 +#define RD_RIGHTBUTTONUP 0x08 +*/ +//------------------------------------------------------------------------------------ +// local function prototypes + +//uint8 Check_sprite_pixel( uint32 j ); +void CreatePointerText(uint32 TextId, uint32 pointerRes); // James16jun97 +void ClearPointerText(void); // James16jun97 +void Monitor_player_activity(void); // James23july97 + +int32 FN_no_human(int32 *params); +void No_human(void); + +//------------------------------------------------------------------------------------ +void Reset_mouse_list(void) //Tony26Sept96 +{ +//call at beginning of gameloop + + cur_mouse=1; +} +//------------------------------------------------------------------------------------ +void Mouse_engine(void) //Tony30Sept96 +{ + + Monitor_player_activity(); // James23july97 + + ClearPointerText(); // James16jun97 + + + if (DEAD) //George is dead ;) + { + if (mouse_mode!=MOUSE_system_menu) + { + mouse_mode=MOUSE_system_menu; + if (mouse_touching) + { +// get off + old_mouse_touching=0; //we've moved off + mouse_touching=0; //we were on something but not anymore + } + + Set_mouse(NORMAL_MOUSE_ID); + + Build_system_menu(); //Tony19Mar97 + } + System_menu(); + + return; + } + + + if (mouse_status) //no human + return; + + + switch(mouse_mode) + { + case MOUSE_normal: //0 normal in game + Normal_mouse(); + break; + + case MOUSE_top: //1 top menu down + Top_menu_mouse(); + break; + + case MOUSE_drag: //2 dragging luggage + Drag_mouse(); + break; + + case MOUSE_system_menu: //3 in game bottom menu - save/restore, etc? + System_menu(); + break; + + case MOUSE_holding: //4 wait for mouse to move off bottom menu - after speech + if (mousey<400) + { mouse_mode=MOUSE_normal; + Zdebug(" releasing"); + } + break; + + default: + break; + } + +} +//------------------------------------------------------------------------------------ +void System_menu(void) //Tony19Mar97 +{ + uint32 safe_looping_music_id; + _mouseEvent *me; + int j,hit; + uint8 *icon; + uint32 rv; // for drivers return value + int32 pars[2]; + uint32 icon_list[5] = + { + OPTIONS_ICON, + QUIT_ICON, + SAVE_ICON, + RESTORE_ICON, + RESTART_ICON + }; + + + + if ((mousey>0)&&(!DEAD)) //can't close when player is dead + { mouse_mode=MOUSE_normal; //close menu + +// start the menu coming down + HideMenu(RDMENU_TOP); + + return; + } + + me = MouseEvent(); //get mouse event + + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed + { + +// clicked on a top mouse pointer? + + if ((mousex>=24)&&(mousex<640-24)&&(mousey<0)) + { + hit=(mousex-24)/40; //which are we over? + + if ((hit==2)&&(DEAD)) + return; //no save when dead + + if (hit<5) //there are 5 system menus + { for (j=0;j<5;j++) //build them all high in full colour - when on eis clicked on all the rest will grey out + if (j!=hit) //change all others to grey + { + icon = res_man.Res_open( icon_list[j] ) + sizeof(_standardHeader); + SetMenuIcon(RDMENU_TOP, j, icon); + res_man.Res_close( icon_list[j] ); + } + + //------------------------ + rv = PauseFx(); + if (rv != RD_OK) + Zdebug("ERROR: PauseFx() returned %.8x in SystemMenu()", rv); + //------------------------ + // NB. Need to keep a safe copy of 'looping_music_id' for savegame + // & for playing when returning from control panels + // because control panel music will overwrite it! + safe_looping_music_id = looping_music_id; + pars[0] = 221; // SystemM234 (M234.wav) + pars[1] = FX_LOOP; + FN_play_music(pars); + looping_music_id = safe_looping_music_id; // restore proper looping_music_id + //------------------------ + //------------------------ + // clear the screen & set up the new palette for the menus + + EraseBackBuffer(); + ProcessMenu(); // drivers to redraw menu over this blank screen! + FlipScreens(); + ResetRenderEngine(); + //------------------------ + + switch(hit) //call the relevent screen + { + case 0: // options + Option_control(); // game options + break; + + case 1: // quit + Quit_control(); // quit to windows + break; + + case 2: // save + Save_control(); // save the game + break; + + case 3: // restore + Restore_control(); // restore a game + break; + + case 4: // restart + Restart_control(); // restart the game + break; + + } + //------------------------ +// start the menu coming down + if (!DEAD) //not death screen + { mouse_mode=MOUSE_normal; //close menu + HideMenu(RDMENU_TOP); //but not when dead + } + else + { Set_mouse(NORMAL_MOUSE_ID); + Build_system_menu(); //reset top menu + } + //------------------------ + // clear the screen & restore the location palette + + EraseBackBuffer(); + ProcessMenu(); // drivers to redraw menu over this blank screen! + FlipScreens(); + + //------------------------ + // reset game palette, but not after a successful restore or restart! + + if (this_screen.new_palette != 99) // see RestoreFromBuffer() in save_rest.cpp + { + SetFullPalette(0); // '0' means put back game screen palette; see Build_display.cpp (James17jun97) + this_screen.new_palette=0; //stop the engine fading in the restored screens palette + } + else + this_screen.new_palette=1; + + //------------------------ + rv = UnpauseFx(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseFx() returned %.8x in SystemMenu()", rv); + //------------------------ + // If there was looping music before coming into the control panels then restart it! + // NB. This will also start music required when a game has been restored + + if (looping_music_id) + { + pars[0] = looping_music_id; + pars[1] = FX_LOOP; + FN_play_music(pars); + // cross-fades into the required music: + // - either a restored game tune + // - or music playing prior to entering control panels + } + else + FN_stop_music(NULL); // stop the control panel music + //------------------------ + } + } + } + +} +//------------------------------------------------------------------------------------ +void Drag_mouse(void) //Tony21Nov96 +{ + _mouseEvent *me; + uint32 pos; +// uint32 null_pc=1; //script 1 is combine script + + + if ((mousey<400)&&(!menu_status)) + { mouse_mode=MOUSE_normal; //close menu + +// start the menu coming down + HideMenu(RDMENU_BOTTOM); + + return; + } + + + Mouse_on_off(); //handles cursors and the luggage on/off according to type + + + +// now do the normal click stuff + + me = MouseEvent(); //get mouse event + + +// we only care about left clicks when the mouse is over an object +// we ignore mouse releases + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed + { + +// could be clicking on an on screen object or on the top menu which is currently displayed + + if (mouse_touching) //mouse is over an on screen object - and we have luggage + { //depending on type we'll maybe kill the object_held - like for exits + if (me->buttons&RD_LEFTBUTTONDOWN) // set global script variable 'button' + { + LEFT_BUTTON = 1; + RIGHT_BUTTON = 0; + } + else + { + LEFT_BUTTON = 0; + RIGHT_BUTTON = 1; + } + + MOUSE_X=(uint32)mousex+this_screen.scroll_offset_x; + MOUSE_Y=(uint32)mousey+this_screen.scroll_offset_y; //these might be required by the action script about to be run + + CLICKED_ID = mouse_touching; // for scripts to know what's been clicked (21jan97). First used for 'room_13_turning_script' in object 'biscuits_13' + + + + Set_player_action_event(CUR_PLAYER_ID, mouse_touching); //Tony4Dec96 + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"USED \"%s\" ICON ON %s", FetchObjectName(OBJECT_HELD), FetchObjectName(CLICKED_ID)); + #endif + //-------------------------------------- + + + HideMenu(RDMENU_BOTTOM); //hide menu too + mouse_mode=MOUSE_normal; //back to normal menu mode + } + else //better check for combine/cancel cancel puts us back in Top_menu_mouse mode + { + if ((mousex>=24)&&(mousex<640-24)) + { + pos=(mousex-24)/40; //which are we over? + + if (master_menu_list[pos].icon_resource) //clicked on something - what button? + { + mouse_mode=MOUSE_top; //always back into top menu mode + + Set_luggage(0); // remove luggage + + if (pos==menu_selected_pos) // if we've clicked on the same icon as the one we're dragging + { + OBJECT_HELD=0; // reset first icon + menu_selected_pos=0; + } + else // combine the 2 icons + { +// Zdebug("combine"); + COMBINE_BASE=master_menu_list[pos].icon_resource; //what we clicked on, not what we're dragging + + Set_player_action_event(CUR_PLAYER_ID, MENU_MASTER_OBJECT); //Tony4Dec96 + + No_human(); // turn off mouse now, to prevent player trying to click elsewhere BUT leave the bottom menu open + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"USED \"%s\" ICON ON \"%s\" ICON", FetchObjectName(OBJECT_HELD), FetchObjectName(COMBINE_BASE)); + #endif + //-------------------------------------- + } + + Build_top_menu(); // refresh the menu + + +// Zdebug("switch to top mode"); + } + } + } + } + + +} +//------------------------------------------------------------------------------------ +void Top_menu_mouse(void) //Tony3Oct96 +{ +//top menu is down + _mouseEvent *me; + uint32 pos; + + if ((mousey<400)&&(!menu_status)) + { mouse_mode=MOUSE_normal; //close menu + +// start the menu coming down + HideMenu(RDMENU_BOTTOM); + + return; + } + + me=MouseEvent(); //get mouse event + + + +// we only care about left clicks when the mouse is over an object +// we ignore mouse releases + + if (me!=NULL) //there's a mouse event to be processed + { +// now check if we've clicked on an actual icon + + if ((mousex>=24)&&(mousex<640-24)) + { + pos=(mousex-24)/40; //which are we over? + + if (master_menu_list[pos].icon_resource) //clicked on something - what button? + { + + if (me->buttons&RD_RIGHTBUTTONDOWN) //right button look + { + examining_menu_icon=1; + OBJECT_HELD = master_menu_list[pos].icon_resource; //id the object via its graphic + EXIT_CLICK_ID=0; // (JEL09oct97) must clear this so next click on exit becomes 1st click again + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"RIGHT-CLICKED ON \"%s\" ICON", FetchObjectName(OBJECT_HELD)); + #endif + //-------------------------------------- + + Set_player_action_event(CUR_PLAYER_ID, MENU_MASTER_OBJECT); //Tony4Dec96 + + Build_top_menu(); // refresh the menu + No_human(); // turn off mouse now, to prevent player trying to click elsewhere BUT leave the bottom menu open + } + else if (me->buttons&RD_LEFTBUTTONDOWN) //left button - highlight the object and bung us into drag luggage mode + { + menu_selected_pos=pos; //menu slot we clicked on - derive luggage resource from this in mouse_on_off() + current_luggage_resource=master_menu_list[pos].luggage_resource; + + mouse_mode=MOUSE_drag; +// Zdebug("setting OH in top menu"); + OBJECT_HELD = master_menu_list[pos].icon_resource; //id the object via its graphic + EXIT_CLICK_ID=0; // (JEL09oct97) must clear this so next click on exit becomes 1st click again + + Build_top_menu(); // refresh the menu + + Set_luggage(master_menu_list[pos].luggage_resource); + +// Zdebug("switch to drag mode"); + } + } + } + } +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void Normal_mouse(void) //Tony30Sept96 +{ +//the gane is playing and none of the menus are activated - but, we need to check if the top menu is to start +//note, wont have luggage + + _mouseEvent *me; + + + + + if ((mousey<0)&&(!menu_status)&&(!mouse_mode_locked)&&(!OBJECT_HELD)) //no save in big-object menu lock situation + { + mouse_mode=MOUSE_system_menu; + + if (mouse_touching) + { +// get off + old_mouse_touching=0; //we've moved off + mouse_touching=0; //we were on something but not anymore + + } + +// reset mouse cursor - in case we're between mice + Set_mouse(NORMAL_MOUSE_ID); + + Build_system_menu(); //Tony19Mar97 + } + + + + + + if ((mousey>399)&&(!menu_status)&&(!mouse_mode_locked)) + { + + + if (!OBJECT_HELD) //why are we testing for this? + { + mouse_mode=MOUSE_top; //bring down top menu + } + else + { + mouse_mode=MOUSE_drag; + } + +// if mouse is moving off an object and onto the top menu then do a standard get-off + if (mouse_touching) + { +// get off + + old_mouse_touching=0; //we've moved off + mouse_touching=0; //we were on something but not anymore + + } + +// reset mouse cursor + Set_mouse(NORMAL_MOUSE_ID); + +// build menu and start the menu coming down + + Build_top_menu(); + + return; + } + + +//check also for bringing the bottom menu up + + + + + Mouse_on_off(); //handles + + + +// now do the normal click stuff + + me = MouseEvent(); //get mouse event + + //----------------------------------------------------- +#ifdef _DEBUG + if (definingRectangles) + { + if (draggingRectangle==0) // not yet dragging a rectangle, so need click to start + { + if ( (me!=NULL) && ((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN)) ) + { + rect_x1 = rect_x2 = (uint32)mousex+this_screen.scroll_offset_x; // set both (x1,y1) + rect_y1 = rect_y2 = (uint32)mousey+this_screen.scroll_offset_y; // & (x2,y2) to this point + draggingRectangle=1; + } + } + else if (draggingRectangle==1) // currently dragging a rectangle + { + if ( (me!=NULL) && ((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN)) ) // click means reset + { + draggingRectangle=2; // lock rectangle, so you can let go of mouse to type in the coords + } + else // drag rectangle + { + rect_x2 = (uint32)mousex+this_screen.scroll_offset_x; + rect_y2 = (uint32)mousey+this_screen.scroll_offset_y; + } + } + else // currently locked to avoid knocking out of place while reading off the coords + { + if ( (me!=NULL) && ((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN)) ) // click means reset + { + draggingRectangle=0; // back to start again + } + } + } + else +#endif // _DEBUG + //----------------------------------------------------- + { + + // we only care about down clicks when the mouse is over an object + // we ignore mouse releases + + if ((me!=NULL)&&((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN))&&(mouse_touching)) //there's a mouse event to be processed and the mouse is on something + { +// ok, there are no menus about so its nice and simple +// this is as close to the old advisor_188 script as we get I'm sorry to say. + + +// if player is walking or relaxing then those need to terminate correctly + + +// otherwise set player run the targets action script +// or, do a special walk if clicking on the scroll-more icon + +// PLAYER_ACTION = mouse_touching; //PLAYER_ACTION script variable - whatever catches this must reset to 0 again + //idle or router-anim will catch it + + + if (me->buttons&RD_LEFTBUTTONDOWN) // set global script variable 'button' + { + LEFT_BUTTON = 1; + RIGHT_BUTTON = 0; + button_click=0; //for re-click + } + else + { + LEFT_BUTTON = 0; + RIGHT_BUTTON = 1; + button_click=1; //for re-click + } + + MOUSE_X=(uint32)mousex+this_screen.scroll_offset_x; + MOUSE_Y=(uint32)mousey+this_screen.scroll_offset_y; //these might be required by the action script about to be run + + + if ((mouse_touching==EXIT_CLICK_ID)&&(me->buttons&RD_LEFTBUTTONDOWN)) //only left button + { +// its the exit double click situation +// let the existing interaction continue and start fading down - switch the human off too + + FN_no_human(NULL); + FN_fade_down(NULL); + EXIT_FADING=1; //tell the walker + + } + else if ((old_button==button_click)&&(mouse_touching==CLICKED_ID)&&(mouse_pointer_res!=NORMAL_MOUSE_ID)) + { //re-click - do nothing - except on floors + } + else //allow the click + { + old_button=button_click; //for re-click + + CLICKED_ID = mouse_touching; // for scripts to know what's been clicked (21jan97). First used for 'room_13_turning_script' in object 'biscuits_13' + EXIT_CLICK_ID=0; //must clear these two double-click control flags - do it here so reclicks after exit clicks are cleared up + EXIT_FADING=0; + Set_player_action_event(CUR_PLAYER_ID, mouse_touching); //Tony4Dec96 + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + if (OBJECT_HELD) + Zdebug(0,"USED \"%s\" ICON ON %s", FetchObjectName(OBJECT_HELD), FetchObjectName(CLICKED_ID)); + else if (LEFT_BUTTON) + Zdebug(0,"LEFT-CLICKED ON %s", FetchObjectName(CLICKED_ID)); + else // RIGHT BUTTON + Zdebug(0,"RIGHT-CLICKED ON %s", FetchObjectName(CLICKED_ID)); + #endif + //-------------------------------------- + } + } + } +} +//------------------------------------------------------------------------------------ +void Mouse_on_off(void) //Tony30Sept96 +{ + //this handles the cursor graphic when moving on and off mouse areas + //it also handles the luggage thingy + + uint32 pointer_type; + static uint8 mouse_flicked_off=0; + + + old_mouse_touching=mouse_touching; + + + if ((mousey<0)||(mousey>399)) // don't detect objects that are hidden behind the menu bars (ie. in the scrolled-off areas of the screen) + { + pointer_type=0; + mouse_touching=0; + } + else + pointer_type = Check_mouse_list(); // set 'mouse_touching' & return pointer_type + + + + + if ((!mouse_flicked_off)&&(old_mouse_touching==mouse_touching)) //same as previous cycle? + return; //yes, so nothing to do BUT CARRY ON IF MOUSE WAS FLICKED OFF! + + mouse_flicked_off=0; // can reset this now + + + if ((!old_mouse_touching)&&(mouse_touching)) //the cursor has moved onto something + { + +// make a copy of the object we've moved onto +// because one day we'll move back off again! (but the list positioning could theoretically have changed) +// we can only move onto something from being on nothing - we stop the system going from one to another when objects overlap + +// memcpy( &old_mouse_object, &mouse_list[mouse_touching], sizeof(Object_mouse)); + + old_mouse_touching=mouse_touching; // + +// run get on + + if (pointer_type) + { + Set_mouse(pointer_type); // 'pointer_type' holds the resource id of the pointer anim + + if (OBJECT_HELD) // setup luggage icon +// Set_luggage(master_menu_list[menu_selected_pos].luggage_resource); + Set_luggage(current_luggage_resource); + + } + else + Con_fatal_error("ERROR: mouse.pointer==0 for object %d (%s) - update logic script!", mouse_touching, FetchObjectName(mouse_touching)); + } + else if ((old_mouse_touching)&&(!mouse_touching)) // the cursor has moved off something + { + old_mouse_touching=0; // we've moved off + Set_mouse(NORMAL_MOUSE_ID); // reset cursor to normal pointer + // reset luggage only when necessary + } + else if ((old_mouse_touching)&&(mouse_touching)) // the cursor has moved off something and onto something else + { // flip to a blank cursor for a cycle + mouse_touching=0; // ignore the new id this cycle - should hit next cycle + old_mouse_touching=0; // we've moved off + Set_mouse(NULL); // blank cursor + mouse_flicked_off=1; // so we know to set the mouse pointer back to normal if 2nd hot-spot doesn't register because mouse pulled away quickly (onto nothing) + + // reset luggage only when necessary + } + else // for when mouse was flicked off for one cycle, but then moved onot nothing before 2nd hot-spot registered + { + // both 'old_mouse_touching' & 'mouse_touching' will be zero + Set_mouse(NORMAL_MOUSE_ID); // reset cursor to normal pointer + } + + // possible check for edge of screen more-to-scroll here on large screens + +} +//------------------------------------------------------------------------------------ +void Set_mouse(uint32 res) // (4dec96 JEL) +{ + uint8 *icon; + uint32 len; + + mouse_pointer_res=res; //high level - whats the mouse - for the engine + + if (res) // if it's not NULL + { + icon = res_man.Res_open( res ) + sizeof(_standardHeader); + len = res_man.resList[res]->size - sizeof(_standardHeader); + + if (res == NORMAL_MOUSE_ID) // don't pulse the normal pointer + SetMouseAnim(icon, len, RDMOUSE_NOFLASH); // 0 means don't pulse this pointer, just do the regular anim loop + else + SetMouseAnim(icon, len, RDMOUSE_FLASH); // 1 mean pulse before starting regular anim loop + + res_man.Res_close( res ); + } + else + SetMouseAnim(NULL,0,0); // blank cursor +} +//------------------------------------------------------------------------------------ +void Set_luggage(uint32 res) //Tony26Nov96 +{ + uint8 *icon; + uint32 len; + + if (res) // if not NULL + { + real_luggage_item=res; + + icon = res_man.Res_open( res ) + sizeof(_standardHeader); + len = res_man.resList[res]->size - sizeof(_standardHeader); + + SetLuggageAnim(icon, len); + + res_man.Res_close( res ); + } + else + { real_luggage_item=0; + SetLuggageAnim(NULL, 0); + } +} +//------------------------------------------------------------------------------------ +uint32 Check_mouse_list(void) //Tony30Sept96 +{ + int32 priority=0; + uint32 j=1; + + + if (cur_mouse>1) + { + while(priority<10) //number of priorities subject to implementation needs + { + if ((mouse_list[j].priority==priority) && // if the mouse pointer is over this mouse-detection-box + (mousex+this_screen.scroll_offset_x >= mouse_list[j].x1) && + (mousex+this_screen.scroll_offset_x <= mouse_list[j].x2) && + (mousey+this_screen.scroll_offset_y >= mouse_list[j].y1) && + (mousey+this_screen.scroll_offset_y <= mouse_list[j].y2)) + { + +/* + if (mouse_list[j].anim_resource) // want to use sprite as a mouse mask, for better accuracy of detection (25oct96 JEL) + { + // only works for uncompressed sprite data!! + // THIS IS NEVER USED IN SWORD2 + + if (Check_sprite_pixel(j)) // if the mouse is touching a non-zero pixel of the sprite + { + mouse_touching=mouse_list[j].id; // record id + CreatePointerText(mouse_list[j].pointer_text); // James16jun97 + return(mouse_list[j].pointer); //return pointer type + } + } + else // ok, we're touching the detection-box +*/ + { + mouse_touching=mouse_list[j].id; // record id + + // change all COGS pointers to CROSHAIR + if (mouse_list[j].pointer == USE) + mouse_list[j].pointer = CROSHAIR; + CreatePointerText(mouse_list[j].pointer_text, mouse_list[j].pointer); // James16jun97 + return(mouse_list[j].pointer); //return pointer type + } + } + + j++; //next + if (j==cur_mouse) + { + j=0; + priority++; //next priority - 0 being the highest, 9 the lowest + } + } + } + + mouse_touching=0; // touching nothing + return(0); // no pointer to return +} +//------------------------------------------------------------------------------------ + +void CreatePointerText(uint32 textId, uint32 pointerRes) // James16jun97 +{ + uint32 local_text; + uint32 text_res; + uint8 *text; + int16 xOffset, yOffset; // offsets for pointer text sprite from pointer position + uint8 justification; + + #define POINTER_TEXT_WIDTH 640 // just in case! + #define POINTER_TEXT_PEN 184 // white + + if (pointerTextSelected) + { + if (textId) + { + //------------------------------------------- + // check what the pointer is, to set offsets correctly for text position + + switch(pointerRes) + { + case CROSHAIR: + yOffset = -7; // above (above & to the right of the pointer coordinate) + xOffset = +10; // right + break; + + case EXIT0: + yOffset = +15; // below + xOffset = +20; // right + break; + + case EXIT1: + yOffset = +16; // below + xOffset = -10; // left + break; + + case EXIT2: + yOffset = +10; // below + xOffset = -22; // left + break; + + case EXIT3: + yOffset = -16; // above + xOffset = -10; // left + break; + + case EXIT4: + yOffset = -15; // above + xOffset = +15; // right + break; + + case EXIT5: + yOffset = -12; // above + xOffset = +10; // right + break; + + case EXIT6: + yOffset = +10; // below + xOffset = +25; // right + break; + + case EXIT7: + yOffset = +16; // below + xOffset = +20; // right + break; + + case EXITDOWN: + yOffset = -20; // above + xOffset = -10; // left + break; + + case EXITUP: + yOffset = +20; // below + xOffset = +20; // right + break; + + case MOUTH: + yOffset = -10; // above + xOffset = +15; // right + break; + + case NORMAL: + yOffset = -10; // above + xOffset = +15; // right + break; + + case PICKUP: + yOffset = -40; // above + xOffset = +10; // right + break; + + case SCROLL_L: + yOffset = -20; // above + xOffset = +20; // right + break; + + case SCROLL_R: + yOffset = -20; // above + xOffset = -20; // left + break; + + case USE: + yOffset = -8; // above + xOffset = +20; // right + break; + + default: // shouldn't happen if we cover all the different mouse pointers above + yOffset = -10; // above + xOffset = +10; // right + } + + //------------------------------------------- + // set up justification for text sprite, + // based on it's offsets from the pointer position + + // from maketext.h + //#define NO_JUSTIFICATION 0 // only for debug text, since it doesn't keep text inside the screen margin! + //#define POSITION_AT_CENTRE_OF_BASE 1 // these all force text inside the screen edge margin when necessary + //#define POSITION_AT_CENTRE_OF_TOP 2 + //#define POSITION_AT_LEFT_OF_TOP 3 + //#define POSITION_AT_RIGHT_OF_TOP 4 + //#define POSITION_AT_LEFT_OF_BASE 5 + //#define POSITION_AT_RIGHT_OF_BASE 6 + //#define POSITION_AT_LEFT_OF_CENTRE 7 + //#define POSITION_AT_RIGHT_OF_CENTRE 8 + + //----------------------------------------- + if (yOffset < 0) // if above pointer + { + if (xOffset < 0) // above left + { + justification = POSITION_AT_RIGHT_OF_BASE; + } + else if (xOffset > 0) // above right + { + justification = POSITION_AT_LEFT_OF_BASE; // text sprite is justified from it's bottom-left corner + } + else // (xOffset==0) // above centre + { + justification = POSITION_AT_CENTRE_OF_BASE; + } + } + //----------------------------------------- + else if (yOffset > 0) // if below pointer + { + if (xOffset < 0) // below left + { + justification = POSITION_AT_RIGHT_OF_TOP; + } + else if (xOffset > 0) // below right + { + justification = POSITION_AT_LEFT_OF_TOP; + } + else // (xOffset==0) // below centre + { + justification = POSITION_AT_CENTRE_OF_TOP; + } + } + //----------------------------------------- + else // if at same y-coord as pointer + { + if (xOffset < 0) // centre left + { + justification = POSITION_AT_RIGHT_OF_CENTRE; + } + else if (xOffset > 0) // centre right + { + justification = POSITION_AT_LEFT_OF_CENTRE; + } + else // (xOffset==0) // centre centre + { + justification = POSITION_AT_LEFT_OF_CENTRE; // shouldn't happen anyway! + } + } + //------------------------------------------- + + text_res = textId/SIZE; // text resource number + local_text = textId&0xffff; // text line number within the resource + + text = FetchTextLine( res_man.Res_open(text_res), local_text ); // open text file & get the line + + // 'text+2' to skip the first 2 bytes which form the line reference number + pointer_text_bloc_no = Build_new_block(text+2, mousex+xOffset, mousey+yOffset, POINTER_TEXT_WIDTH, POINTER_TEXT_PEN, RDSPR_TRANS+RDSPR_DISPLAYALIGN, speech_font_id, justification); + + res_man.Res_close(text_res); // now ok to close the text file + } + } +} +//------------------------------------------------------------------------------------ +void ClearPointerText(void) // James16jun97 +{ + if (pointer_text_bloc_no) + { + Kill_text_bloc(pointer_text_bloc_no); + pointer_text_bloc_no=0; + } +} +//------------------------------------------------------------------------------------ +/* NOT USED IN SWORD2 +uint8 Check_sprite_pixel( uint32 j ) // (25oct96 JEL) +{ + // only works for uncompressed sprite data!! + + uint8 *file; + uint8 hit; + _frameHeader *frame_head; + int16 sprite_x, sprite_y; + int16 in_sprite_x, in_sprite_y; + uint8 *sprite_data; + + + sprite_x = mouse_list[j].x1; // sprite coords have been copied to mouse area coords by FN_register_X_frame (X = sort, back or fore) + sprite_y = mouse_list[j].y1; // so easier to get them from there than from the anim file again + + + file = res_man.Res_open(mouse_list[j].anim_resource); // open the anim file & point to start of it + frame_head = FetchFrameHeader(file,mouse_list[j].anim_pc); // point to frame header of current frame + sprite_data = (uint8 *)(frame_head + 1); // point to start of frame data + + in_sprite_x = mousex + this_screen.scroll_offset_x - sprite_x; // x-coord of mouse from origin at top-left of sprite + in_sprite_y = mousey + this_screen.scroll_offset_y - sprite_y; // y-coord of mouse from origin at top-left of sprite + + hit = sprite_data[in_sprite_y * frame_head->width + in_sprite_x]; // hit = value of pixel to which the mouse is pointing + + res_man.Res_close(mouse_list[j].anim_resource); // close anim file + + return (hit); // we are touching the sprite if 'hit' is non-zero +} +*/ +//------------------------------------------------------------------------------------ +int32 FN_no_human(int32 *params) //Tony30Sept96 +{ +//param none + + MOUSE_AVAILABLE = 0; // for logic scripts (James21may97) + + ClearPointerText(); + + mouse_status=1; //human/mouse off + + Set_mouse(NULL); // blank cursor + Set_luggage(NULL); // blank cursor + + +//must be normal mouse situation or a largely neutral situation - special menus use No_human + if (TALK_FLAG==0) //dont hide menu in conversations + HideMenu(RDMENU_BOTTOM); + + + if (mouse_mode==MOUSE_system_menu) + { + mouse_mode=MOUSE_normal; //close menu + HideMenu(RDMENU_TOP); // start the menu coming down + } + + + if (params); + + + return(1); //script continue +} +//------------------------------------------------------------------------------------ +void No_human(void) //Tony4June97 +{ +//leaves the menus open +//used by the system when clicking right on a menu item to examine it and +//when combining objects + + MOUSE_AVAILABLE = 0; // for logic scripts (James21may97) + + mouse_status=1; //human/mouse off + + Set_mouse(NULL); // blank cursor + Set_luggage(NULL); // blank cursor +} +//------------------------------------------------------------------------------------ +int32 FN_add_human(int32 *params) //Tony30Sept96 +{ + //param none +#ifdef _DEBUG + uint8 black[4] = {0,0,0,0}; + uint8 white[4] = {255,255,255,0}; +#endif // ('0' means don't print to console, but console isn't up anyway) + + + + MOUSE_AVAILABLE = 1; // for logic scripts (James21may97) + + + if (mouse_status) //off + { + mouse_status=0; //on + mouse_touching=1; //forces engine to choose a cursor + } + + CLICKED_ID=0; //clear this to reset no-second-click system + + // this is now done outside the OBJECT_HELD check in case it's set to zero before now! (James 10july97) + mouse_mode_locked=0; //unlock the mouse from possible large object lock situtations - see syphon in rm 3 + + if (OBJECT_HELD) //was dragging something around + { + OBJECT_HELD=0; // need to clear this again + + examining_menu_icon=0; // and these may also need clearing + COMBINE_BASE=0; // - just in case + + + Set_luggage(NULL); // blank cursor + } + + + + if ((mousey>399)&&(mouse_mode!=MOUSE_holding)) // if mouse is over menu area + { + mouse_mode=MOUSE_normal; // VITAL - reset things & rebuild the menu + Set_mouse(NORMAL_MOUSE_ID); + } + else if (mousey>399) + Set_mouse(NORMAL_MOUSE_ID); + + + //---------------------------------------------------------------------------------------------- + // enabled/disabled from console; status printed with on-screen debug info +#ifdef _DEBUG + if (testingSnR) // testing logic scripts by simulating an instant Save & Restore + { + SetPalette(0, 1, white, RDPAL_INSTANT); + + Clear_fx_queue(); // stops all fx & clears the queue - eg. when leaving a location + res_man.Kill_all_objects(0); // ie. trashing all object resources so they load in fresh & restart their logic scripts + + SetPalette(0, 1, black, RDPAL_INSTANT); + } +#endif // ('0' means don't print to console, but console isn't up anyway) + //---------------------------------------------------------------------------------------------- + if (params); + + return(1); //script continue +} +//------------------------------------------------------------------------------------ +int32 FN_register_mouse(int32 *params) //Tony29Oct96 +{ +//this call would be made from an objects service script 0 +//the object would be one with no graphic but with a mouse - i.e. a floor +// or one whose mouse area is manually defined rather than intended to fit sprite shape + +//param 0 pointer to Object_mouse or 0 for no write to mouse list + +// Zdebug(1,"cur_mouse = %d", cur_mouse); + + + Object_mouse *ob_mouse; + + ob_mouse = (Object_mouse *) params[0]; // param 1 is pointer to mouse structure + + if (ob_mouse->pointer) // only if 'pointer' isn't NULL + { + #ifdef _DEBUG + if (cur_mouse==TOTAL_mouse_list) + Con_fatal_error("ERROR: mouse_list full [%s line %u]",__FILE__,__LINE__); + #endif + + mouse_list[cur_mouse].x1 = ob_mouse->x1; + mouse_list[cur_mouse].y1 = ob_mouse->y1; + mouse_list[cur_mouse].x2 = ob_mouse->x2; + mouse_list[cur_mouse].y2 = ob_mouse->y2; + + mouse_list[cur_mouse].priority = ob_mouse->priority; + mouse_list[cur_mouse].pointer = ob_mouse->pointer; + + //----------------------------------------------- + // (James17jun97) + // check if pointer text field is set due to previous object using this slot (ie. not correct for this one) + if ((mouse_list[cur_mouse].pointer_text) && (mouse_list[cur_mouse].id != (int32)ID)) // if 'pointer_text' field is set, but the 'id' field isn't same is current id + mouse_list[cur_mouse].pointer_text=0; // then we don't want this "left over" pointer text + //----------------------------------------------- + + mouse_list[cur_mouse].id = ID; // get id from system variable 'id' which is correct for current object + + mouse_list[cur_mouse].anim_resource = 0; // not using sprite as mask - this is only done from FN_register_frame() + mouse_list[cur_mouse].anim_pc = 0; + + //Zdebug("mouse id %d", mouse_list[cur_mouse].id); + + cur_mouse++; + } + + return(IR_CONT); // continue script +} +//------------------------------------------------------------------------------------ +// use this in the object's service script prior to registering the mouse area +// ie. before FN_register_mouse or FN_register_frame +// - best if kept at very top of service script + +int32 FN_register_pointer_text(int32 *params) // James16jun97 +{ +// param 0 local id of text line to use as pointer text + + #ifdef _DEBUG + if (cur_mouse==TOTAL_mouse_list) + Con_fatal_error("ERROR: mouse_list full [%s line %u]",__FILE__,__LINE__); + #endif + + mouse_list[cur_mouse].id = ID; // current object id - used for checking pointer_text when mouse area registered (in FN_register_mouse & FN_register_frame) + mouse_list[cur_mouse].pointer_text = params[0]; + + return(IR_CONT); // continue script + +} +//------------------------------------------------------------------------------------ +int32 FN_blank_mouse(int32 *params) //Tony29Oct96 +{ +//set mouse to normal pointer - used in speech + +//no params + + + Set_mouse(NULL); + + if (params); + + return(1); //cont +} +//------------------------------------------------------------------------------------ +int32 FN_init_floor_mouse(int32 *params) // James29nov96 +{ + // params 0 pointer to object's mouse structure + + Object_mouse *ob_mouse = (Object_mouse *) params[0]; + + ob_mouse->x1 = 0; + ob_mouse->y1 = 0; + ob_mouse->x2 = this_screen.screen_wide-1; + ob_mouse->y2 = this_screen.screen_deep-1; + ob_mouse->priority = 9; // floor is always lowest priority + ob_mouse->pointer = NORMAL_MOUSE_ID; // normal pointer + + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------------------------- +#define SCROLL_MOUSE_WIDTH 20 // James13feb97 (updated by James 25mar97) +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_set_scroll_left_mouse(int32 *params) // James13feb97 +{ + // params 0 pointer to object's mouse structure + + Object_mouse *ob_mouse = (Object_mouse *) params[0]; + + + ob_mouse->x1 = 0; + ob_mouse->y1 = 0; + ob_mouse->x2 = this_screen.scroll_offset_x + SCROLL_MOUSE_WIDTH; + ob_mouse->y2 = this_screen.screen_deep-1; + ob_mouse->priority = 0; // highest priority + + if (this_screen.scroll_offset_x > 0) // if not fully scrolled to the left + ob_mouse->pointer = SCROLL_LEFT_MOUSE_ID; + else + ob_mouse->pointer = 0; // so the mouse area doesn't get registered + + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_set_scroll_right_mouse(int32 *params) // James13feb97 +{ + // params 0 pointer to object's mouse structure + + Object_mouse *ob_mouse = (Object_mouse *) params[0]; + + + ob_mouse->x1 = this_screen.scroll_offset_x + screenWide - SCROLL_MOUSE_WIDTH; + ob_mouse->y1 = 0; + ob_mouse->x2 = this_screen.screen_wide-1; + ob_mouse->y2 = this_screen.screen_deep-1; + ob_mouse->priority = 0; // highest priority + + if (this_screen.scroll_offset_x < this_screen.max_scroll_offset_x) // if not fully scrolled to the right + ob_mouse->pointer = SCROLL_RIGHT_MOUSE_ID; + else + ob_mouse->pointer = 0; // so the mouse area doesn't get registered + + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_set_object_held(int32 *params) //tony19May97 +{ +//params 0 luggage icon to set + + Set_luggage(params[0]); + + OBJECT_HELD=params[0]; + current_luggage_resource=params[0]; + + mouse_mode_locked=1; //mode locked - no top menu available + + + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +// called from speech scripts to remove the chooser bar when it's not appropriate to keep it displayed +int32 FN_remove_chooser(int32 *params) // James13aug97 +{ + HideMenu(RDMENU_BOTTOM); + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_disable_menu(int32 *params) //Tony1Oct96 +{ + mouse_mode_locked=1; //mode locked - no top menu available + mouse_mode=MOUSE_normal; + + HideMenu(RDMENU_TOP); + HideMenu(RDMENU_BOTTOM); + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_enable_menu(int32 *params) //tony4June97 +{ + + + mouse_mode_locked=0; //mode locked - no top menu available + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_check_player_activity(int32 *params) // James23july97 +{ +// Used to decide when to trigger music cues described as "no player activity for a while" + +// params 0 threshold delay in seconds, ie. what we want to check the actual delay against + + uint32 threshold = params[0]*12; // in game cycles + + if (player_activity_delay >= threshold) // if the actual delay is at or above the given threshold + { + player_activity_delay=0; // reset activity delay counter, now that we've got a positive check + RESULT=1; + } + else + RESULT=0; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_reset_player_activity_delay(int32 *params) // James23july97 +{ +// Use if you want to deliberately reset the "no player activity" counter for any reason + +// no params + + player_activity_delay=0; // reset activity delay counter + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +void Monitor_player_activity(void) // James23july97 +{ + if (CheckForMouseEvents()) // if there is at least one mouse event outstanding + player_activity_delay=0; // reset activity delay counter + else + player_activity_delay++; // no. of game cycles since mouse event queue last empty +} +//--------------------------------------------------------------------------------------------------------------------- + diff --git a/sword2/mouse.h b/sword2/mouse.h new file mode 100644 index 0000000000..3845818875 --- /dev/null +++ b/sword2/mouse.h @@ -0,0 +1,89 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//mouse stuff + +#ifndef MOUSE_H +#define MOUSE_H + +//#include "src\driver96.h" +#include "object.h" + +//--------------------------------------------------------------------------------- +#define TOTAL_mouse_list 50 + +#define MOUSE_normal 0 +#define MOUSE_top 1 +#define MOUSE_drag 2 +#define MOUSE_system_menu 3 +#define MOUSE_holding 4 +//--------------------------------------------------------------------------------- +// mouse unit - like Object_mouse, but with anim resource & pc (needed if sprite is to act as mouse detection mask) +typedef struct +{ + int32 x1; // top-left of mouse area is (x1,y1) + int32 y1; + int32 x2; // bottom-right of area is (x2,y2) (these coords are inclusive) + int32 y2; + int32 priority; + int32 pointer; // type (or resource id?) of pointer used over this area + // up to here, this is basically a copy of the Object_mouse structure, but then we have... + int32 id; // object id, used when checking mouse list + int32 anim_resource; // resource id of animation file (if sprite to be used as mask) - otherwise 0 + int32 anim_pc; // current frame number of animation + int32 pointer_text; // local id of text line to print when pointer highlights an object +} Mouse_unit; +//--------------------------------------------------------------------------------- +extern uint32 cur_mouse; +extern Mouse_unit mouse_list[TOTAL_mouse_list]; +extern uint32 mouse_touching; +extern uint32 mouse_mode; +extern uint8 examining_menu_icon; + +extern uint32 mouse_status; //human 0 on/1 off +extern uint32 mouse_mode_locked; //0 not !0 mode cannot be changed from normal mouse to top menu (i.e. when carrying big objects) + +extern uint32 real_luggage_item; //last minute for pause mode + +extern uint32 pointerTextSelected; + +//--------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------- +void Reset_mouse_list(void); //Tony26Sept96 + +void Normal_mouse(void); //Tony30Sept96 +void Top_menu_mouse(void); //Tony3Oct96 +void Drag_mouse(void); //Tony21Nov96 +void System_menu(void); //Tony19Mar97 + +void Mouse_on_off(void); //Tony30Sept96 +uint32 Check_mouse_list(void); //Tony30Sept96 +void Mouse_engine(void); //Tony30Sept96 + +void Set_mouse(uint32 res); +void Set_luggage(uint32 res); //Tony26Nov96 + +int32 FN_no_human(int32 *params); //Tony30Sept96 +int32 FN_add_human(int32 *params); //Tony30Sept96 + +void ClearPointerText(void); // James16jun97 +//--------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------- +#endif diff --git a/sword2/object.h b/sword2/object.h new file mode 100644 index 0000000000..b5e5763c77 --- /dev/null +++ b/sword2/object.h @@ -0,0 +1,124 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _SCRIPT_STRUCTURES +#define _SCRIPT_STRUCTURES + +#include "driver/driver96.h" + +// these structures represent the broken up compact components +// these here declared to the system must be the same as those declared to LINC (or it wont work) + + +// mouse structure - defines mouse detection area, detection priority & 'type' flag +typedef struct +{ + int32 x1; // top-left of mouse area is (x1,y1) + int32 y1; + int32 x2; // bottom-right of area is (x2,y2) (these coords are inclusive) + int32 y2; + int32 priority; + int32 pointer; // type (or resource id?) of pointer used over this area +} Object_mouse; + + +// logic structure - contains fields used in logic script processing +typedef struct +{ + int32 looping; // 0 when first calling FN_<function>; 1 when calling subsequent times in same loop + int32 pause; // pause count, used by FN_pause() +} Object_logic; + +//------------------------------------------------ +// status bits for 'type' field of Object_graphic) +// in low word: +#define NO_SPRITE 0x00000000 // don't print +#define BGP0_SPRITE 0x00000001 // fixed to background parallax[0] +#define BGP1_SPRITE 0x00000002 // fixed to background parallax[1] +#define BACK_SPRITE 0x00000004 // 'background' sprite, fixed to main background +#define SORT_SPRITE 0x00000008 // 'sorted' sprite, fixed to main background +#define FORE_SPRITE 0x00000010 // 'foreground' sprite, fixed to main background +#define FGP0_SPRITE 0x00000020 // fixed to foreground parallax[0] +#define FGP1_SPRITE 0x00000040 // fixed to foreground parallax[0] + +// in high word: +#define UNSHADED_SPRITE 0x00000000 // not to be shaded +#define SHADED_SPRITE 0x00010000 // to be shaded, based on shading mask +//------------------------------------------------ + +// graphic structure - contains fields appropriate to sprite output +typedef struct +{ + int32 type; // see above + int32 anim_resource; // resource id of animation file + int32 anim_pc; // current frame number of animation +} Object_graphic; + + +// speech structure - contains fields used by speech scripts & text output +typedef struct +{ + int32 pen; // colour to use for body of characters + int32 width; // max width of text sprite + int32 command; // speech script command id + int32 ins1; // speech script instruction parameters (may need more now?) + int32 ins2; + int32 ins3; + int32 ins4; + int32 ins5; + int32 wait_state; //0 not waiting 1 waiting for next speech command +} Object_speech; + + +// mega structure - contains fields used for mega-character & mega-set processing +typedef struct +{ + int32 NOT_USED_1; // only free roaming megas need to check this before registering their graphics for drawing + int32 NOT_USED_2; // id of floor on which we are standing + int32 NOT_USED_3; // id of object which we are getting to + int32 NOT_USED_4; // pixel distance to stand from player character when in conversation + int32 currently_walking; // number given us by the auto router + int32 walk_pc; // current frame number of walk-anim + int32 scale_a; // current scale factors, taken from floor data + int32 scale_b; + int32 feet_x; // mega feet coords - frame-offsets are added to these position mega frames + int32 feet_y; + int32 current_dir; // current dirction faced by mega; used by autorouter to determine turns required + int32 colliding; // means were currently avoiding a collision (see FN_walk) + int32 megaset_res; // resource id of mega-set file + int32 NOT_USED_5; // NOT USED +} Object_mega; + + +// walk-data structure - contains details of layout of frames in the mega-set, and how they are to be used +typedef struct +{ + int32 nWalkFrames; // no. of frames per walk-cycle + int32 usingStandingTurnFrames; // 0=no 1=yes + int32 usingWalkingTurnFrames; // 0=no 1=yes + int32 usingSlowInFrames; // 0=no 1=yes + int32 usingSlowOutFrames; // 0=no !0=number of slow-out frames in each direction + int32 nSlowInFrames[8]; // no. of slow-in frames in each direction + int32 leadingLeg[8]; // leading leg for walk in each direction (0=left 1=right) + int32 dx[8*(12+1)]; // walk step distances in x direction + int32 dy[8*(12+1)]; // walk step distances in y direction +} Object_walkdata; + + +#endif diff --git a/sword2/protocol.cpp b/sword2/protocol.cpp new file mode 100644 index 0000000000..bfeb804dfe --- /dev/null +++ b/sword2/protocol.cpp @@ -0,0 +1,240 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include <stdio.h> +//#include <windows.h> + +//#include "src\driver96.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "logic.h" +#include "memory.h" +#include "protocol.h" +#include "resman.h" + +//----------------------------------------------------------------------------------------------------------------------- +// returns a pointer to the first palette entry, given the pointer to the start of the screen file +// assumes it has been passed a pointer to a valid screen file +uint8 *FetchPalette(uint8 *screenFile) // Chris 04Oct96 +{ + uint8 *palette; + + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + + palette = (uint8 *)mscreenHeader + mscreenHeader->palette; + + palette[0] = 0; // always set colour 0 to black + palette[1] = 0; // because most background screen palettes have a bright colour 0 + palette[2] = 0; // although it should come out as black in the game! + palette[3] = 0; + + return palette; +} +//----------------------------------------------------------------------------------------------------------------------- +// returns a pointer to the start of the palette match table, given the pointer to the start of the screen file +// assumes it has been passed a pointer to a valid screen file + +uint8 *FetchPaletteMatchTable(uint8 *screenFile) // James 09dec96 +{ + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + + return (uint8 *) mscreenHeader + mscreenHeader->paletteTable; +} + +//----------------------------------------------------------------------------------------------------------------------- +// returns a pointer to the screen header, given the pointer to the start of the screen file +// assumes it has been passed a pointer to a valid screen file +_screenHeader *FetchScreenHeader(uint8 *screenFile) //Chris 04Oct96 +{ + // Get the table + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + + return (_screenHeader*) ((uint8 *) mscreenHeader + mscreenHeader->screen); + +} +//----------------------------------------------------------------------------------------------------------------------- +// returns a pointer to the requested layer header, given the pointer to the start of the screen file +// drops out if the requested layer number exceeds the number of layers on this screen +// assumes it has been passed a pointer to a valid screen file +_layerHeader *FetchLayerHeader(uint8 *screenFile, uint16 layerNo) //Chris 04Oct96 +{ + _screenHeader *screenHead; + + + screenHead = FetchScreenHeader(screenFile); + +#ifdef _DEBUG + if (layerNo > (screenHead->noLayers-1)) // layer number too large! + Con_fatal_error("FetchLayerHeader(%d) invalid layer number! (%s line %u)",layerNo,__FILE__,__LINE__); +#endif + + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + + return (_layerHeader *) ((uint8 *) mscreenHeader + mscreenHeader->layers + (layerNo * sizeof(_layerHeader))); +} + +//--------------------------------------------------------------- +// returns a pointer to the start of the shading mask, given the pointer to the start of the screen file +// assumes it has been passed a pointer to a valid screen file + +uint8 *FetchShadingMask(uint8 *screenFile) // James 08apr97 +{ + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + + return (uint8 *) mscreenHeader + mscreenHeader->maskOffset; +} + +//----------------------------------------------------------------------------------------------------------------------- +// returns a pointer to the anim header, given the pointer to the start of the anim file +// assumes it has been passed a pointer to a valid anim file + +_animHeader *FetchAnimHeader(uint8 *animFile) // (25sep96JEL) +{ + return (_animHeader *) (animFile + sizeof(_standardHeader)); +} + +//--------------------------------------------------------------- +// returns a pointer to the requested frame number's cdtEntry, given the pointer to the start of the anim file +// drops out if the requested frame number exceeds the number of frames in this anim +// assumes it has been passed a pointer to a valid anim file + +_cdtEntry *FetchCdtEntry(uint8 *animFile, uint16 frameNo) // Chris 09Oct96 +{ + _animHeader *animHead; + + animHead = FetchAnimHeader(animFile); + +#ifdef _DEBUG + if (frameNo > (animHead->noAnimFrames-1)) // frame number too large! + Con_fatal_error("FetchCdtEntry(animFile,%d) - anim only %d frames (%s line %u)",frameNo,animHead->noAnimFrames,__FILE__,__LINE__); +#endif + + return (_cdtEntry *) ( (uint8 *)animHead + sizeof(_animHeader) + frameNo * sizeof(_cdtEntry) ); +} +//--------------------------------------------------------------- +// returns a pointer to the requested frame number's header, given the pointer to the start of the anim file +// drops out if the requested frame number exceeds the number of frames in this anim +// assumes it has been passed a pointer to a valid anim file + +_frameHeader *FetchFrameHeader(uint8 *animFile, uint16 frameNo) // James 31oct96 +{ + // required address = (address of the start of the anim header) + frameOffset + return (_frameHeader *) (animFile + sizeof(_standardHeader) + (FetchCdtEntry(animFile,frameNo)->frameOffset) ); +} +//--------------------------------------------------------------- +// Returns a pointer to the requested parallax layer data. +// Assumes it has been passed a pointer to a valid screen file. +_parallax *FetchBackgroundParallaxLayer(uint8 *screenFile, int layer) // Chris 04Oct96 +{ + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + +#ifdef _DEBUG + if (mscreenHeader->bg_parallax[layer] == 0) + Con_fatal_error("FetchBackgroundParallaxLayer(%d) - No parallax layer exists (%s line %u)",layer,__FILE__,__LINE__); +#endif + + return (_parallax *) ((uint8 *) mscreenHeader + mscreenHeader->bg_parallax[layer]); +} +//--------------------------------------------------------------- +_parallax *FetchBackgroundLayer(uint8 *screenFile) // Chris 04Oct96 +{ + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + +#ifdef _DEBUG + if (mscreenHeader->screen == 0) + Con_fatal_error("FetchBackgroundLayer (%d) - No background layer exists (%s line %u)",__FILE__,__LINE__); +#endif + + return (_parallax *) ((uint8 *) mscreenHeader + mscreenHeader->screen + sizeof(_screenHeader)); +} +//--------------------------------------------------------------- +_parallax *FetchForegroundParallaxLayer(uint8 *screenFile, int layer) // Chris 04Oct96 +{ + _multiScreenHeader *mscreenHeader = (_multiScreenHeader *) (screenFile + sizeof(_standardHeader)); + +#ifdef _DEBUG + if (mscreenHeader->fg_parallax[layer] == 0) + Con_fatal_error("FetchForegroundParallaxLayer(%d) - No parallax layer exists (%s line %u)",layer,__FILE__,__LINE__); +#endif + + return (_parallax *) ((uint8 *) mscreenHeader + mscreenHeader->fg_parallax[layer]); +} +//--------------------------------------------------------------- +uint8 errorLine[128]; +//--------------------------------------------------------------- +uint8 *FetchTextLine(uint8 *file, uint32 text_line) //Tony24Oct96 +{ + // Get the table + _standardHeader *fileHeader; + uint32 *point; + + + _textHeader *text_header = (_textHeader *) (file + sizeof(_standardHeader)); + + + if (text_line>=text_header->noOfLines) // (James08aug97) + { + fileHeader = (_standardHeader*)file; + sprintf ((char*)errorLine, "xxMissing line %d of %s (only 0..%d)", text_line, fileHeader->name, text_header->noOfLines-1); + errorLine[0]=0; // first 2 chars are NULL so that actor-number comes out as '0' + errorLine[1]=0; + return(errorLine); + +// GOT RID OF CON_FATAL_ERROR HERE BECAUSE WE DON'T WANT IT TO CRASH OUT ANY MORE! +// Con_fatal_error("FetchTextLine cannot get %d, only 0..%d avail (%s line %u)", text_line, text_header->noOfLines-1,__FILE__,__LINE__); + } + + + point=(uint32*) text_header+1; //point to the lookup table + + return( (uint8*) (file+ *(point+text_line)) ); +} +//--------------------------------------------------------------- +// Used for testing text & speech (see FN_I_speak in speech.cpp) +uint8 CheckTextLine(uint8 *file, uint32 text_line) // (James26jun97) +{ + _textHeader *text_header = (_textHeader *) (file + sizeof(_standardHeader)); + + if (text_line>=text_header->noOfLines) + return(0); // out of range => invalid + else + return(1); // valid +} +//--------------------------------------------------------------- +uint8 *FetchObjectName(int32 resourceId) // James15jan97 +{ + _standardHeader *header; + + header = (_standardHeader*) res_man.Res_open(resourceId); + + res_man.Res_close(resourceId); + + return (header->name); // note this pointer is no longer valid, but it should be ok until another resource is opened! +} +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//--------------------------------------------------------------- diff --git a/sword2/protocol.h b/sword2/protocol.h new file mode 100644 index 0000000000..97751f0881 --- /dev/null +++ b/sword2/protocol.h @@ -0,0 +1,44 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//the usual suspects (the usual suspicions) + +#ifndef _PROTOCOL +#define _PROTOCOL + +#include "driver/driver96.h" +#include "header.h" + +uint8 *FetchPalette(uint8 *screenFile); // Chris 04Oct96 +_screenHeader *FetchScreenHeader(uint8 *screenFile); //Chris 04Oct96 +_layerHeader *FetchLayerHeader(uint8 *screenFile, uint16 layerNo); //Chris 04Oct96 +uint8 *FetchShadingMask(uint8 *screenFile); // James 08apr97 + +_animHeader *FetchAnimHeader(uint8 *animFile); // (25sep96JEL) +_cdtEntry *FetchCdtEntry(uint8 *animFile, uint16 frameNo); // (31oct96 JEL) +_frameHeader *FetchFrameHeader(uint8 *animFile, uint16 frameNo); // (25sep96JEL) +_parallax *FetchBackgroundParallaxLayer(uint8 *screenFile, int layer); // Chris 04Oct96 +_parallax *FetchBackgroundLayer(uint8 *screenFile); // Chris 04Oct96 +_parallax *FetchForegroundParallaxLayer(uint8 *screenFile, int layer); // Chris 04Oct96 +uint8 *FetchTextLine(uint8 *file, uint32 text_line); //Tony24Oct96 +uint8 CheckTextLine(uint8 *file, uint32 text_line); // (James26jun97) +uint8 *FetchPaletteMatchTable(uint8 *screenFile); // James 09dec96 +uint8 *FetchObjectName(int32 resourceId); // James15jan97 + +#endif diff --git a/sword2/resman.cpp b/sword2/resman.cpp new file mode 100644 index 0000000000..37afab5a4d --- /dev/null +++ b/sword2/resman.cpp @@ -0,0 +1,1539 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include <process.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "engine.h" + +#include "driver/driver96.h" +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "function.h" +#include "header.h" +#include "interpreter.h" +#include "maketext.h" +#include "memory.h" +#include "mouse.h" // for system Set_mouse & Set_luggage routines +#include "protocol.h" +#include "resman.h" +#include "sound.h" // (James22july97) for Clear_fx_queue() called from CacheNewCluster() +#include "sword2.h" // (James11aug97) for CloseGame() + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//welcome to the easy resource manager - written in simple code for easy maintenance + +//the resource compiler will create two files + +// resource.inf which is a list of ascii cluster file names +// resource.tab which is a table which tells us which cluster a resource is located in and the number within the cluster + + +//------------------------------------------------------------------------------------ + +#define NONE 0 +#define FETCHING 1 + +#define BUFFERSIZE 4096 + + +resMan res_man; //declare the object global + + +//------------------------------------------------------------------------------------ +// +// +// resman. +// +// +//------------------------------------------------------------------------------------ +#define BOTH 0x0 // Cluster is on both CDs +#define CD1 0x1 // Cluster is on CD1 only +#define CD2 0x2 // Cluster is on CD2 only +#define LOCAL_CACHE 0x4 // Cluster is cached on HDD +#define LOCAL_PERM 0x8 // Cluster is on HDD. + + +typedef struct +{ + uint8 clusterName[20]; // Null terminated cluster name. + uint8 cd; // Cd cluster is on and whether it is on the local drive or not. +} _cd_inf; + +//------------------------------------------------------------------------------------ + +void resMan::InitResMan(void) //Tony29May96 +{ +//we read in the resource info which tells us the names of the resource cluster files +//ultimately, although there might be groups within the clusters at this point it makes no difference. +//we only wish to know what resource files there are and what is in each + + File file; + uint32 end; + mem *temp; + uint32 pos=0; + uint32 j=0; + + + total_clusters=0; + + + if (file.open("resource.inf", g_bs2->getGameDataPath()) == false) { + Zdebug("InitResMan cannot *OPEN* resource.inf"); + ExitWithReport("InitResMan cannot *OPEN* resource.inf [file=%s line=%u]",__FILE__,__LINE__); + } + + end = file.size(); + +// Zdebug("seek end %d %d", fh, end); + + temp = Twalloc(end, MEM_locked, UID_temp); //get some space for the incoming resource file - soon to be trashed + + file.seek(0, SEEK_SET ); + if (file.read( temp->ad, end )==-1) + { + file.close(); + Zdebug("InitResMan cannot *READ* resource.inf"); + ExitWithReport("InitResMan cannot *READ* resource.inf [file=%s line=%u]",__FILE__,__LINE__); + } + + file.close(); + + +//ok, we've loaded in the resource.inf file which contains a list of all the files +//now extract the filenames + do + { + while(*(temp->ad+j)!=13) //item must have an #0d0a + { resource_files[total_clusters][pos]=*(temp->ad+j); + j++; + pos++; + }; + + resource_files[total_clusters][pos]=0; //NULL terminate our extracted string + + pos=0; //reset position in current slot between entries + j+=2; //past the 0a + total_clusters++; //done another + +// put overload check here + + + } + while(j!=end); //using this method the Gode generated resource.inf must have #0d0a on the last entry + + + + + +//now load in the binary id to res conversion table + if (file.open("resource.tab", g_bs2->getGameDataPath()) == false) { + Zdebug("InitResMan cannot *OPEN* resource.tab"); + ExitWithReport("InitResMan cannot *OPEN* resource.tab [file=%s line=%u]",__FILE__,__LINE__); + } + + +//find how many resources + end = file.size(); + file.seek( 0, SEEK_SET); // Back to the beginning of the file + + total_res_files=end/4; + +//table seems ok so malloc some space + res_conv_table = (uint16 *) malloc( end ); + + if (file.read( res_conv_table, end )==-1) + { + file.close(); + Zdebug("InitResMan cannot *READ* resource.tab"); + ExitWithReport("InitResMan cannot *READ* resource.tab [file=%s line=%u]",__FILE__,__LINE__); + } + file.close(); + + + if (file.open("cd.inf", g_bs2->getGameDataPath()) == false) { + Zdebug("InitResMan cannot *OPEN* cd.inf"); + ExitWithReport("InitResMan cannot *OPEN* cd.inf [file=%s line=%u]",__FILE__,__LINE__); + } + + + _cd_inf *cdInf = new _cd_inf[total_clusters]; + + for (j=0;j<total_clusters;j++) + { + if (file.read(&cdInf[j], sizeof(_cd_inf)) != sizeof(_cd_inf)) + { + Zdebug("InitResMan failed to read cd.inf. Insufficient entries?"); + ExitWithReport("InitResMan failed to read cd.inf. Insufficient entries? [file=%s line=%u]",__FILE__,__LINE__); + } + } + + file.close(); + + for (j=0; j<total_clusters; j++) + { + uint32 i=0; + + while((scumm_stricmp((char *) cdInf[i].clusterName, resource_files[j]) != 0) && (i<total_clusters)) + i++; + + if (i == total_clusters) + { + Zdebug("InitResMan, %s is not in cd.inf", resource_files[j]); + ExitWithReport("InitResMan, %s is not in cd.inf [file=%s line=%u]",resource_files[j],__FILE__,__LINE__); + } + else + cdTab[j] = cdInf[i].cd; + } + + + Zdebug("\n%d resources in %d cluster files", total_res_files, total_clusters); + for (j=0;j<total_clusters;j++) + Zdebug("filename of cluster %d: -%s", j, resource_files[j]); + Zdebug(""); + + + resList = (mem **) malloc( total_res_files * sizeof(mem *)); //create space for a list of pointers to mem's + + + age = (uint32 *) malloc( total_res_files * sizeof(uint32) ); + +// status = (uint16 *) malloc( total_res_files * sizeof(uint16) ); + + count = (uint16 *) malloc( total_res_files * sizeof(uint16) ); + + + for (j=0;j<total_res_files;j++) + { age[j]=0; //age must be 0 if the file is not in memory at all + count[j]=0; + } + + resTime=1; //cannot start at 0 + + Free_mem(temp); //get that memory back + +/* we don't have to worry about BSODs here - khalek :) + // Stop that nasty blue screen?? + SetErrorMode(SEM_FAILCRITICALERRORS); +*/ + + + if (file.open("revcd1.id", g_bs2->getGameDataPath()) == false) { + int index = 0; +/* + // Scan for CD drives. + for (char c='C'; c<='Z'; c++) + { + sprintf(cdPath, "%c:\\", c); + if (GetDriveType(cdPath) == DRIVE_CDROM) + cdDrives[index++] = c; + } +*/ + cdDrives[index++] = 'C'; + + if (index == 0) + { + Zdebug("InitResMan, cannot find CD drive."); + ExitWithReport("InitResMan, cannot find CD drive. [file=%s line=%u]",__FILE__,__LINE__); + } + + while (index<24) + cdDrives[index++] = 0; + } + else + file.close(); +} + +//------------------------------------------------------------------------------------ + +char *resMan::GetCdPath(void) +{ + return cdPath; +} + +//------------------------------------------------------------------------------------ + +void resMan::Close_ResMan(void) //Tony29May96 +{ +//free up our mallocs + + + free(resList); + free(age); + +// status = (uint16 *) malloc( total_res_files * sizeof(uint16) ); + + free(count); +} + +//------------------------------------------------------------------------------------ +uint8 *resMan::Res_open( uint32 res ) //BHTony30May96 +{ +//returns ad of resource. Loads if not in memory +//retains a count +//resource can be aged out of memory if count=0 +//the resource is locked while count!=0 i.e. until a res_close is called + + File file; + uint16 parent_res_file; + uint16 actual_res; + uint32 pos,len; + + uint32 table_offset; + + int first=0; + + +#ifdef _DEBUG + if (res>=total_res_files) + Con_fatal_error("Res_open illegal resource %d (there are %d resources 0-%d)", res, total_res_files, total_res_files-1); +#endif + +//is the resource in memory already? + if (!age[res]) //if the file is not in memory then age should and MUST be 0 + { +// fetch the correct file and read in the correct portion +// if the file cannot fit then we must trash the oldest large enough floating file + + parent_res_file = res_conv_table[res*2]; //points to the number of the ascii filename + +#ifdef _DEBUG + if (parent_res_file==0xffff) + Con_fatal_error("Res_open tried to open null & void resource number %d", res); +#endif + + actual_res= res_conv_table[(res*2)+1]; //relative resource within the file + +// first we have to find the file via the res_conv_table + +// Zdebug("resOpen %s res %d", resource_files[parent_res_file], res); +// ** at this point here we start to think about where the file is and prompt the user for the right CD to be inserted ** +// ** we need to know the position that we're at within the game - LINC should write this someplace. + + + if (!(cdTab[parent_res_file] & LOCAL_CACHE) && !(cdTab[parent_res_file] & LOCAL_PERM)) + { + // This cluster is on a CD, we need to cache a new one. + CacheNewCluster(parent_res_file); + } + else if (!(cdTab[parent_res_file] & LOCAL_PERM)) + { + GetCd(cdTab[parent_res_file] & 3); // Makes sure that the correct CD is in the drive. + } + + //open the cluster file + if (file.open(resource_files[parent_res_file], g_bs2->getGameDataPath()) == false) + Con_fatal_error("Res_open cannot *OPEN* %s", resource_files[parent_res_file]); + + + //1st DWORD of a cluster is an offset to the look-up table + table_offset = file.readUint32LE(); + + + //Zdebug("table offset = %d", table_offset); + + file.seek( (table_offset+(actual_res*8)), SEEK_SET); //2 dwords per resource + file.read( &pos, 4); //get position of our resource within the cluster file + file.read( &len, 4); //read the length + + file.seek(pos, SEEK_SET); // ** get to position in file of our particular resource + +// Zdebug("res len %d", len); + +// ok, we know the length so try and allocate the memory +// if it can't then old files will be ditched until it works + resList[res] = Twalloc(len, MEM_locked, res); + + // Do a quick ServiceWindows to stop the music screwing up. + ServiceWindows(); + +// now load the file + file.read( resList[res]->ad, len); //hurray, load it in. + + file.close(); //close the cluster + } + else + { +// Zdebug("RO %d, already open count=%d", res, count[res]); + } + + + + count[res]++; //number of times opened - the file won't move in memory while count is non zero + + age[res]=resTime; //update the accessed time stamp - touch the file in other words + + + + + Lock_mem( resList[res] ); //pass the address of the mem & lock the memory too + //might be locked already (if count>1) + + return( (uint8 *) resList[res]->ad ); +} + +//------------------------------------------------------------------------------------ +uint8 resMan::Res_check_valid( uint32 res ) // James 12mar97 +{ + // returns '1' if resource is valid, otherwise returns '0' + // used in startup.cpp to ignore invalid screen-manager resources + + uint16 parent_res_file; + + + if (res>=total_res_files) + return(0); // resource number out of range + + parent_res_file = res_conv_table[res*2]; // points to the number of the ascii filename + + if (parent_res_file==0xffff) + return(0); // null & void resource + + return(1); // ok +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + +void resMan::Res_next_cycle( void ) //Tony8Feb97 +{ +//increment the cycle and calculate actual per-cycle memory useage + +#ifdef _DEBUG + uint32 j; +#endif + + + +#ifdef _DEBUG + current_memory_useage=0; + + for (j=1;j<total_res_files;j++) + if ( age[j]==resTime ) //was accessed last cycle + current_memory_useage += resList[j]->size; +#endif + + resTime++; + + if (!resTime) + resTime++; //if you left the game running for a hundred years when this went to 0 + //there'd be a resource left stuck in memory - after another hundred years there'd be another... + + +} +//------------------------------------------------------------------------------------ +uint32 resMan::Res_fetch_useage( void ) //Tony8Feb97 +{ +//returns memory usage previous cycle + + return(current_memory_useage); +} +//------------------------------------------------------------------------------------ + + + + +void resMan::Res_close( uint32 res ) //Tony30May96 +{ +//decrements the count +//resource floats when count=0 + + +#ifdef _DEBUG + if (res>=total_res_files) + Con_fatal_error("Res_closeing illegal resource %d (there are %d resources 0-%d)", res, total_res_files, total_res_files-1); + + if (!(count[res])) //closing but isnt open? + Con_fatal_error("Res_close closing %d but it isn't open", res); +#endif + + + count[res]--; //one less has it open + + if (!count[res]) //if noone has the file open then unlock and allow to float + { + Float_mem( resList[res] ); //pass the address of the mem + +// *(status+res)-=RES_locked; //unlock the resource + + } + + +} + +//------------------------------------------------------------------------------------ +uint32 resMan::Res_fetch_len( uint32 res ) //Tony27Jan96 +{ +//returns the total file length of a resource - i.e. all headers are included too + + FILE *fh=0; //file pointer + uint16 parent_res_file; + uint16 actual_res; + uint32 len; + uint32 table_offset; + + + parent_res_file = res_conv_table[res*2]; //points to the number of the ascii filename + + actual_res= res_conv_table[(res*2)+1]; //relative resource within the file + +// first we have to find the file via the res_conv_table + + + fh = fopen(resource_files[parent_res_file],"rb"); //open the cluster file + if (fh==NULL) + Con_fatal_error("Res_fetch_len cannot *OPEN* %s", resource_files[parent_res_file]); + + + fread( &table_offset, sizeof(char), sizeof(uint32), fh); //1st DWORD of a cluster is an offset to the look-up table + + + fseek(fh, table_offset+(actual_res*8)+4, SEEK_SET); //2 dwords per resource + skip the position dword + //fread( &pos, sizeof(char), 4, fh); //get position of our resource within the cluster file + fread( &len, sizeof(char), 4, fh); //read the length + + + return(len); + +} + + +//------------------------------------------------------------------------------------ +char *resMan::Fetch_cluster( uint32 res) //Tony3June96 +{ +//returns a pointer to the ascii name of the cluster file which contains resource res + + return(resource_files[res_conv_table[res*2]]); +} +//------------------------------------------------------------------------------------ +uint32 resMan::Fetch_age(uint32 res) //Tony3June96 +{ +//return the age of res + + return(age[res]); +} +//------------------------------------------------------------------------------------ +uint32 resMan::Fetch_count(uint32 res) //Tony3June96 +{ +//return the open count of res + + return(count[res]); +} +//------------------------------------------------------------------------------------ +uint32 resMan::Help_the_aged_out(void) //Tony10Oct96 +{ +//remove from memory the oldest closed resource + + uint32 oldest_res; //holds id of oldest found so far when we have to chuck stuff out of memory + uint32 oldest_age; //age of above during search + uint32 j; + uint32 largestResource = 0; + + + oldest_age=resTime; + oldest_res=0; + + for (j=2;j<total_res_files;j++) + if ( (!count[j]) && (age[j]) && (age[j]<=oldest_age)) //not held open and older than this one + { + if ((age[j] == oldest_age) && (resList[j]->size > largestResource)) + { + oldest_res = j; + largestResource = resList[j]->size; // Kick old resource of oldest age and largest size (Helps the poor defragger). + } + else if (age[j] < oldest_age) + { + oldest_res = j; + oldest_age = age[j]; + largestResource = resList[j]->size; + } + } + + if (!oldest_res) //there was not a file we could release + return(0); //no bytes released - oh dear, lets hope this never happens + + +// Zdebug(42,"removing %d, age %d, size %d", oldest_res, age[oldest_res], resList[oldest_res]->size); + +// trash this old resource + + age[oldest_res]=0; //effectively gone from resList + Free_mem(resList[oldest_res]); //release the memory too + + return(resList[oldest_res]->size); //return bytes freed +} +//------------------------------------------------------------------------------------ +void resMan::Print_console_clusters(void) //Tony10Oct96 +{ + uint32 j; + + + if (total_clusters) + { for (j=0;j<total_clusters;j++) + Print_to_console(" %s", resource_files[j]); + + Print_to_console(" %d resources", total_res_files); + } + else + Print_to_console(" argh! No resources"); + + + Scroll_console(); +} +//------------------------------------------------------------------------------------ +void resMan::Examine_res(uint8 *input) //Tony23Oct96 +{ + uint32 j=0; + uint32 res; + _standardHeader *file_header; + + + + do + { if ( (*(input+j)>='0') && (*(input+j)<='9')) + j++; + else + break; + } + while(*(input+j)); + + + if (!*(input+j)) //didn't quit out of loop on a non numeric chr$ + { res = atoi((char*)input); + + if (!res) + Print_to_console("illegal resource"); + + else if(res>=total_res_files) + Print_to_console("illegal resource %d (there are %d resources 0-%d)", res, total_res_files, total_res_files-1); + + else if (res_conv_table[res*2]==0xffff) + Print_to_console("%d is a null & void resource number", res); + + else //open up the resource and take a look inside! + { + file_header = (_standardHeader*) res_man.Res_open(res); + +// Print_to_console("%d", file_header->fileType); +// Print_to_console("%s", file_header->name); + + + //-------------------------------------------------------------------------------- + // resource types: (taken from header.h) + + // 0 something's wrong! +// #define ANIMATION_FILE 1 // all normal animations & sprites including mega-sets & font files which are the same format +// #define SCREEN_FILE 2 // each contains background, palette, layer sprites, parallax layers & shading mask +// #define GAME_OBJECT 3 // each contains object hub + structures + script data +// #define WALK_GRID_FILE 4 // walk-grid data +// #define GLOBAL_VAR_FILE 5 // all the global script variables in one file; "there can be only one" +// #define PARALLAX_FILE_null 6 // NOT USED +// #define RUN_LIST 7 // each contains a list of object resource id's +// #define TEXT_FILE 8 // each contains all the lines of text for a location or a character's conversation script +// #define SCREEN_MANAGER 9 // one for each location; this contains special startup scripts +// #define MOUSE_FILE 10 // mouse pointers and luggage icons (sprites in General \ Mouse pointers & Luggage icons) +// #define ICON_FILE 12 // menu icon (sprites in General \ Menu icons +//---------------------------------------------------------- + + + switch(file_header->fileType) + { + //----------------------- + case ANIMATION_FILE: // 1 + Print_to_console(" <anim> %s", file_header->name); + break; + //----------------------- + case SCREEN_FILE: // 2 + Print_to_console(" <layer> %s", file_header->name); + break; + //----------------------- + case GAME_OBJECT: // 3 + Print_to_console(" <game object> %s", file_header->name); + break; + //----------------------- + case WALK_GRID_FILE: // 4 + Print_to_console(" <walk grid> %s", file_header->name); + break; + //----------------------- + case GLOBAL_VAR_FILE: // 5 + Print_to_console(" <global variables> %s", file_header->name); + break; + //----------------------- + case PARALLAX_FILE_null: // 6 + Print_to_console(" <parallax file NOT USED!> %s", file_header->name); + break; + //----------------------- + case RUN_LIST: // 6 + Print_to_console(" <run list> %s", file_header->name); + break; + //----------------------- + case TEXT_FILE: // 8 + Print_to_console(" <text file> %s", file_header->name); + break; + //----------------------- + case SCREEN_MANAGER: // 9 + Print_to_console(" <screen manager> %s", file_header->name); + break; + //----------------------- + case MOUSE_FILE: // 10 + Print_to_console(" <mouse pointer> %s", file_header->name); + break; + //----------------------- + case ICON_FILE: // 12 + Print_to_console(" <menu icon> %s", file_header->name); + break; + //----------------------- + default: // 0 or >13 + Print_to_console(" unrecognised fileType %d", file_header->fileType); + break; + //----------------------- + } + res_man.Res_close(res); + } + } + else + { + Print_to_console("try typing a number"); + } +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void resMan::Kill_res(uint8 *input) //Tony23Oct96 +{ + int j=0; + uint32 res; + + + + + do + { if ( (*(input+j)>='0') && (*(input+j)<='9')) + j++; + else + break; + } + while(*(input+j)); + + + + if (!*(input+j)) //didn't quit out of loop on a non numeric chr$ + { + res = atoi((char*)input); + + +#ifdef _DEBUG + if (!res) + Print_to_console("illegal resource"); + + if (res>=total_res_files) + Con_fatal_error(" llegal resource %d (there are %d resources 0-%d)", res, total_res_files, total_res_files-1); +#endif + + + if (!count[res]) //if noone has the file open then unlock and allow to float + { + if (age[res]) + { + age[res]=0; //effectively gone from resList + Free_mem(resList[res]); //release the memory too + Print_to_console(" trashed %d", res); + } + else + Print_to_console("%d not in memory", res); + } + else + Print_to_console(" file is open - cannot remove"); + } + else + { + Print_to_console("try typing a number"); + } + + +} +//------------------------------------------------------------------------------------ +void resMan::Remove_res(uint32 res) //Tony10Jan97 +{ + if (age[res]) + { + age[res]=0; //effectively gone from resList + Free_mem(resList[res]); //release the memory too +// Zdebug(" - Trashing %d", res); + } + else + Zdebug("Remove_res(%d) not even in memory!",res); +} +//------------------------------------------------------------------------------------ +void resMan::Remove_all_res(void) // James24mar97 +{ + // remove all res files from memory - ready for a total restart + // including player object & global variables resource + + int j=0; + uint32 res; + + + j=base_mem_block; + + do + { + if (mem_list[j].uid<65536) //a resource + { + res=mem_list[j].uid; + + age[res]=0; //effectively gone from resList + Free_mem(resList[res]); //release the memory too + } + + j=mem_list[j].child; + } + while (j!=-1); +} +//------------------------------------------------------------------------------------ +void resMan::Kill_all_res(uint8 wantInfo) //Tony29Nov96 +{ + // remove all res files from memory + // its quicker to search the mem blocs for res files than search resource lists for those in memory + + int j=0; + uint32 res; + uint32 nuked=0; + _standardHeader *header; + int scrolls=0; + char c; + + + j=base_mem_block; + + do + { + if (mem_list[j].uid<65536) //a resource + { + res=mem_list[j].uid; + + if ((res!=1)&&(res!=CUR_PLAYER_ID)) //not the global vars which are assumed to be open in memory & not the player object! (James17jan97) + { + header = (_standardHeader*) res_man.Res_open(res); + res_man.Res_close(res); + + age[res]=0; //effectively gone from resList + Free_mem(resList[res]); //release the memory too + nuked++; + + if ((wantInfo)&&(console_status)) // if this was called from the console + we want info + { + Print_to_console(" nuked %5d: %s", res, header->name); + Zdebug(" nuked %d: %s", res, header->name); + Build_display(); + + scrolls++; + if (scrolls==18) + { + Temp_print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + //-------------------------------------------------- + } + while(!KeyWaiting()); + + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + + Clear_console_line(); //clear the Press Esc message ready for the new line + scrolls=0; + } + } + } + } + + j=mem_list[j].child; + } + while (j!=-1); + + + if ((wantInfo)&&(console_status)) // if this was called from the console + we want info! + { Scroll_console(); + Print_to_console(" expelled %d resource(s)", nuked); + } +} +//------------------------------------------------------------------------------------ +// Like Kill_all_res but only kills objects (except George & the variable table of course) +// - ie. forcing them to reload & restart their scripts, which simulates the effect +// of a save & restore, thus checking that each object's re-entrant logic works correctly, +// and doesn't cause a statuette to disappear forever, or some plaster-filled holes +// in sand to crash the game & get James in trouble again. + +void resMan::Kill_all_objects(uint8 wantInfo) // James17jan97 +{ + // remove all object res files from memory, excluding George + // its quicker to search the mem blocs for res files than search resource lists for those in memory + + int j=0; + uint32 res; + uint32 nuked=0; + _standardHeader *header; + int scrolls=0; + char c; + + j=base_mem_block; + + do + { + if (mem_list[j].uid<65536) //a resource + { + res=mem_list[j].uid; + + if ((res!=1)&&(res!=CUR_PLAYER_ID)) //not the global vars which are assumed to be open in memory & not the player object! (James17jan97) + { + header = (_standardHeader*) res_man.Res_open(res); + res_man.Res_close(res); + + if (header->fileType == GAME_OBJECT) + { + age[res]=0; //effectively gone from resList + Free_mem(resList[res]); //release the memory too + nuked++; + + if ((wantInfo)&&(console_status)) // if this was called from the console + we want info + { + Print_to_console(" nuked %5d: %s", res, header->name); + Zdebug(" nuked %d: %s", res, header->name); + Build_display(); + + scrolls++; + if (scrolls==18) + { + Print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + //-------------------------------------------------- + } + while(!KeyWaiting()); + + + ReadKey(&c); //kill the key we just pressed + scrolls=0; + } + } + } + } + } + + j=mem_list[j].child; + } + while (j!=-1); + + + if ((wantInfo)&&(console_status)) // if this was called from the console + we want info + Print_to_console(" expelled %d object resource(s)", nuked); +} + +//------------------------------------------------------------------------------------ + +void resMan::CacheNewCluster(uint32 newCluster) +{ + uint8 black[4]={0,0,0,0}; + + //---------------------------------------------------------------------------------------- + // Stop any music from streaming off the CD before we start the cluster-copy! + // - eg. the looping restore-panel music will still be playing if we restored a game + // to a different cluster on the same CD + // - and music streaming would interfere with cluster copying, slowing it right down + // - but if we restored to a different CD the music is stopped in GetCd() when it asks for the CD + + FN_stop_music(NULL); // (James16sep97) + //---------------------------------------------------------------------------------------- + + Clear_fx_queue(); // stops all fx & clears the queue (James22july97) + + GetCd(cdTab[newCluster] & 3); + + // Kick out old cached cluster and load the new one. + uint32 i=0; + while ((!(cdTab[i] & LOCAL_CACHE)) && (i<total_clusters)) + i++; + + if (i<total_clusters) + { + SetFileAttributes(resource_files[i], FILE_ATTRIBUTE_NORMAL); + DeleteFile(resource_files[i]); + cdTab[i] &= (0xff - LOCAL_CACHE); + FILE *file; + file = fopen("cd.inf", "r+b"); + + if (file == NULL) + { + Zdebug("CacheNewCluster cannot *OPEN* cd.inf"); + Con_fatal_error("InitResMan cannot *OPEN* cd.inf [file=%s line=%u]",__FILE__,__LINE__); + } + + _cd_inf cdInf; + + do + { + fread(&cdInf, 1, sizeof(_cd_inf), file); + } while ((scumm_stricmp((char *) cdInf.clusterName, resource_files[i]) != 0) && !feof(file)); + + if (feof(file)) + { + Zdebug("CacheNewCluster cannot find %s in cd.inf", resource_files[i]); + Con_fatal_error("CacheNewCluster cannot find %s in cd.inf", resource_files[i]); + } + + fseek(file, -1, SEEK_CUR); + fwrite(&cdTab[i], 1, 1, file); + fclose(file); + } + + char buf[1024]; + sprintf(buf, "%sClusters\\%s", cdPath, resource_files[newCluster]); + + uint8 fadeStat; + + do + { + fadeStat = GetFadeStatus(); + + + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + + } while ((fadeStat == RDFADE_UP) || (fadeStat == RDFADE_DOWN)); + + if (GetFadeStatus() != RDFADE_BLACK) + { + FadeDown((float) 0.75); + + do + { + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(GetFadeStatus()!=RDFADE_BLACK); + } + + +//get rid of top menu flash (tony 26Aug97) + EraseBackBuffer(); + FlipScreens(); + EraseBackBuffer(); + FlipScreens(); + + + + Set_mouse(NULL); + Set_luggage(0); //tw28Aug + + uint8 *bgfile; + bgfile = res_man.Res_open(2950); // open the screen resource + InitialiseBackgroundLayer(NULL); + InitialiseBackgroundLayer(NULL); + InitialiseBackgroundLayer(FetchBackgroundLayer(bgfile)); + InitialiseBackgroundLayer(NULL); + InitialiseBackgroundLayer(NULL); + SetPalette(0, 256, FetchPalette(bgfile), RDPAL_FADE); + + RenderParallax(FetchBackgroundLayer(bgfile), 2); + res_man.Res_close(2950); // release the screen resource + + SetFileAttributes(resource_files[newCluster], FILE_ATTRIBUTE_NORMAL); // Git rid of read-only status, if it is set. + + FILE *inFile, *outFile; + + inFile = fopen(buf, "rb"); + outFile = fopen(resource_files[newCluster], "wb"); + + if ((inFile == NULL) || (outFile == NULL)) + { + Zdebug("Cache new cluster could not copy %s to %s", buf, resource_files[newCluster]); + Con_fatal_error("Cache new cluster could not copy %s to %s [file=%s line=%u]", buf, resource_files[newCluster],__FILE__,__LINE__); + } + + _spriteInfo textSprite; + _spriteInfo barSprite; + mem *text_spr; + _frameHeader *frame; + uint8 *loadingBar; + _cdtEntry *cdt; + + text_spr = MakeTextSprite( FetchTextLine(res_man.Res_open(2283),8)+2, 640, 187, speech_font_id ); + + frame = (_frameHeader*) text_spr->ad; + + textSprite.x = screenWide/2 - frame->width/2; + textSprite.y = screenDeep/2 - frame->height/2 - RDMENU_MENUDEEP; + textSprite.w = frame->width; + textSprite.h = frame->height; + textSprite.scale = 0; + textSprite.scaledWidth = 0; + textSprite.scaledHeight = 0; + textSprite.type = RDSPR_DISPLAYALIGN+RDSPR_NOCOMPRESSION+RDSPR_TRANS; + textSprite.blend = 0; + textSprite.colourTable = 0; + + res_man.Res_close(2283); + + loadingBar = res_man.Res_open(2951); + + frame = FetchFrameHeader(loadingBar, 0); + cdt = FetchCdtEntry(loadingBar, 0); + + barSprite.x = cdt->x; + barSprite.y = cdt->y; + barSprite.w = frame->width; + barSprite.h = frame->height; + barSprite.scale = 0; + barSprite.scaledWidth = 0; + barSprite.scaledHeight = 0; + barSprite.type = RDSPR_RLE256FAST+RDSPR_TRANS; + barSprite.blend = 0; + barSprite.colourTable = 0; + + res_man.Res_close(2951); + + loadingBar = res_man.Res_open(2951); + frame = FetchFrameHeader(loadingBar, 0); + barSprite.data = (uint8 *) (frame+1); + res_man.Res_close(2951); + + int16 barX = barSprite.x; + int16 barY = barSprite.y; + int16 textX = textSprite.x; + int16 textY = textSprite.y; + + DrawSprite(&barSprite); + barSprite.x = barX; + barSprite.y = barY; + + textSprite.data = text_spr->ad + sizeof(_frameHeader); + DrawSprite(&textSprite); + textSprite.x = textX; + textSprite.y = textY; + + CopyScreenBuffer(); + FlipScreens(); + + FadeUp((float)0.75); + + do + { + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(GetFadeStatus()==RDFADE_UP); + + fseek(inFile, 0, SEEK_END); + uint32 size = ftell(inFile); + fseek(inFile, 0, SEEK_SET); + + char buffer[BUFFERSIZE]; + int stepSize = (size/BUFFERSIZE)/100; + uint32 read = 0; + int step = stepSize; + int fr = 0; + uint32 realRead = 0; + + do + { + realRead = fread(buffer, 1, BUFFERSIZE, inFile); + read += realRead; + if (fwrite(buffer, 1, realRead, outFile) != realRead) + { + Zdebug("Cache new cluster could not copy %s to %s", buf, resource_files[newCluster]); + Con_fatal_error("Cache new cluster could not copy %s to %s [file=%s line=%u]", buf, resource_files[newCluster],__FILE__,__LINE__); + } + + if (step == stepSize) + { + step = 0; + bgfile = res_man.Res_open(2950); // open the screen resource + RenderParallax(FetchBackgroundLayer(bgfile), 2); + res_man.Res_close(2950); // release the screen resource + loadingBar = res_man.Res_open(2951); + frame = FetchFrameHeader(loadingBar, fr); + barSprite.data = (uint8 *) (frame+1); + res_man.Res_close(2951); + DrawSprite(&barSprite); + barSprite.x = barX; + barSprite.y = barY; + + textSprite.data = text_spr->ad + sizeof(_frameHeader); + DrawSprite(&textSprite); + textSprite.x = textX; + textSprite.y = textY; + + CopyScreenBuffer(); + FlipScreens(); + + fr += 1; + } + else + step += 1; + + //-------------------------------------------------- + // Service windows + // NOTE: Carry on even when not got the focus!!! + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + + } while ((read % BUFFERSIZE) == 0); + + if (read != size) + { + Zdebug("Cache new cluster could not copy %s to %s", buf, resource_files[newCluster]); + Con_fatal_error("Cache new cluster could not copy %s to %s [file=%s line=%u]", buf, resource_files[newCluster],__FILE__,__LINE__); + } + + fclose(inFile); + fclose(outFile); + Free_mem(text_spr); + + EraseBackBuffer(); // for hardware rendering + EraseSoftwareScreenBuffer(); // for software rendering + + FadeDown((float)0.75); + + do + { + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(GetFadeStatus()==RDFADE_DOWN); + + CopyScreenBuffer(); + FlipScreens(); + FadeUp((float)0.75); + + SetFileAttributes(resource_files[newCluster], FILE_ATTRIBUTE_NORMAL); // Git rid of read-only status. + + // Update cd.inf and cdTab + cdTab[newCluster] |= LOCAL_CACHE; + + FILE *file; + file = fopen("cd.inf", "r+b"); + + if (file == NULL) + { + Zdebug("CacheNewCluster cannot *OPEN* cd.inf"); + Con_fatal_error("InitResMan cannot *OPEN* cd.inf [file=%s line=%u]",__FILE__,__LINE__); + } + + _cd_inf cdInf; + + do + { + fread(&cdInf, 1, sizeof(_cd_inf), file); + } while ((scumm_stricmp((char *) cdInf.clusterName, resource_files[newCluster]) != 0) && !feof(file)); + + if (feof(file)) + { + Zdebug("CacheNewCluster cannot find %s in cd.inf", resource_files[newCluster]); + Con_fatal_error("CacheNewCluster cannot find %s in cd.inf", resource_files[newCluster]); + } + + fseek(file, -1, SEEK_CUR); + fwrite(&cdTab[newCluster], 1, 1, file); + fclose(file); + + // Now update DelList.log to indicate that this cluster should be removed at uninstall. + file = fopen("DelList.log", "r+"); + + if (file != NULL) + { + fseek(file, -3, SEEK_END); + + char path[_MAX_PATH]; + GetCurrentDirectory(_MAX_PATH, path); + + strcat(path, "\\"); + strcat(path, resource_files[newCluster]); + fwrite(path, 1, strlen(path), file); + + sprintf(path, "\nend"); + fwrite(path, 1, 4, file); + + fclose(file); + } +} + +//------------------------------------------------------------------------------------ + +void resMan::GetCd(int cd) +{ + bool done = false; + char sCDName[_MAX_PATH]; + DWORD dwMaxCompLength, dwFSFlags; + mem *text_spr; + _frameHeader *frame; + _spriteInfo spriteInfo; + int16 oldY; + int16 oldX; + FILE *file; + char name[16]; + int offNetwork = 0; + int index = 0; + uint8 *textRes; + + //---------------------------------------------------------------------------------------- + #ifdef _WEBDEMO // (James 01oct97) + return; // don't ask for CD's in the playable demo downloaded from our web-site! + #endif // _WEBDEMO + + #ifdef _PCGUIDE + return; // don't ask for CD in the patch for the demo on "PC Guide" magazine + #endif + //---------------------------------------------------------------------------------------- + + sprintf(name, "revcd%d.id", cd); + file = fopen(name, "r"); + + if (file == NULL) + { + // Determine what CD is in the drive, and either use it or ask the user to insert the correct CD. + // Scan all CD drives for our CD as well. + while((cdDrives[index] != 0) && (index<24)) + { + sprintf(cdPath, "%c:\\", cdDrives[index]); + + if (!GetVolumeInformation(cdPath, sCDName, _MAX_PATH, NULL, &dwMaxCompLength, &dwFSFlags, NULL, 0)) + { + sCDName[0] = 0; // Force the following code to ask for the correct CD. + } + + curCd = cd; + + if (!scumm_stricmp(sCDName,CD1_LABEL)) + { + if (cd == CD1) + return; + } + else if (!scumm_stricmp(sCDName,CD2_LABEL)) + { + if (cd == CD2) + return; + } + + index += 1; + } + } + else // must be running off the network, but still want to see CD-requests to show where they would occur when playing from CD + { + Zdebug("RUNNING OFF NETWORK"); + + fscanf(file, "%s", cdPath); + fclose(file); + + if (curCd == cd) + return; + else + curCd = cd; + + if (SYSTEM_TESTING_ANIMS || SYSTEM_TESTING_TEXT) // don't show CD-requests if testing anims or text/speech + return; + + offNetwork = 1; + } + + //---------------------------------------------------------------------------------------- + // stop any music from playing - so the system no longer needs the current CD + // - otherwise when we take out the CD, Windows will complain! + + FN_stop_music(NULL); // (James29aug97) + //---------------------------------------------------------------------------------------- + + + textRes = res_man.Res_open(2283); + DisplayMsg( FetchTextLine(textRes, 5+cd)+2, 0 ); + text_spr = MakeTextSprite( FetchTextLine( textRes, 5+cd)+2, 640, 187, speech_font_id ); + + frame = (_frameHeader*) text_spr->ad; + + spriteInfo.x = screenWide/2 - frame->width/2; + spriteInfo.y = screenDeep/2 - frame->height/2 - RDMENU_MENUDEEP; + spriteInfo.w = frame->width; + spriteInfo.h = frame->height; + spriteInfo.scale = 0; + spriteInfo.scaledWidth = 0; + spriteInfo.scaledHeight = 0; + spriteInfo.type = RDSPR_DISPLAYALIGN+RDSPR_NOCOMPRESSION+RDSPR_TRANS; + spriteInfo.blend = 0; + spriteInfo.data = text_spr->ad + sizeof(_frameHeader); + spriteInfo.colourTable = 0; + oldY = spriteInfo.y; + oldX = spriteInfo.x; + + res_man.Res_close(2283); + + do + { + if (offNetwork == 1) + done = TRUE; + else + { + char sCDName[_MAX_PATH]; + DWORD dwMaxCompLength, dwFSFlags; + + index = 0; + while((cdDrives[index] != 0) && (!done) && (index<24)) + { + sprintf(cdPath, "%c:\\", cdDrives[index]); + + if (!GetVolumeInformation(cdPath, sCDName, _MAX_PATH, NULL, &dwMaxCompLength, &dwFSFlags, NULL, 0)) + { + sCDName[0] = 0; + } + + if (!scumm_stricmp(sCDName,CD1_LABEL)) + { + if (cd == CD1) + done = TRUE; + } + else if (!scumm_stricmp(sCDName,CD2_LABEL)) + { + if (cd == CD2) + done = TRUE; + } + + index += 1; + } + } + + //-------------------------------------------------- + // Service windows + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + //-------------------------------------------------- + + if (gotTheFocus) + { + EraseBackBuffer(); // for hardware rendering + EraseSoftwareScreenBuffer(); // for software rendering + DrawSprite( &spriteInfo ); // Keep the message there even when the user task swaps. + spriteInfo.y = oldY; // Drivers change the y co-ordinate, don't know why... + spriteInfo.x = oldX; + CopyScreenBuffer(); + FlipScreens(); + } + + } while (!done); + + Free_mem(text_spr); + RemoveMsg(); +} +//------------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + + diff --git a/sword2/resman.h b/sword2/resman.h new file mode 100644 index 0000000000..fb7a215b1e --- /dev/null +++ b/sword2/resman.h @@ -0,0 +1,101 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef RESMAN_H +#define RESMAN_H + +//#include "src\driver96.h" +#include "memory.h" + +#define MAX_res_files 20 + +#define RES_locked 1 +#define RES_perm 2 + + +class resMan +{ + public: + + void InitResMan(void); //read in the config file + void Close_ResMan(void); //Tony29May96 +//---- + uint8 *Res_open(uint32 res); //returns ad of resource. Loads if not in memory + //retains a count + //resource can be aged out of memory if count=0 + //the resource is locked while count!=0 + + void Res_close(uint32 res); //decrements the count + +//---- + + uint8 Res_check_valid( uint32 res ); // returns '0' if resource out of range or null, otherwise '1' for ok + + //resource floats when count=0 +//---- + char *Fetch_cluster(uint32 res); //for mem_view to query the owners of mem blocs + uint32 Fetch_age(uint32 res); // + uint32 Fetch_count(uint32 count); // + + uint32 Help_the_aged_out(void); //Tony10Oct96 + + uint32 Res_fetch_len( uint32 res ); //Tony27Jan96 + + void Res_next_cycle( void ); + uint32 Res_fetch_useage( void ); + + void GetCd(int cd); // Prompts the user for the specified CD. + int WhichCd() {return curCd;} + +//----console commands + void Print_console_clusters(void); //Tony10Oct96 + void Examine_res(uint8 *input); //Tony23Oct96 + void Kill_all_res(uint8 wantInfo); //Tony29Nov96 + void Kill_all_objects(uint8 wantInfo); // James17jan97 + void Remove_res(uint32 res); //Tony10Jan97 + void Remove_all_res(void); // James24mar97 + void Kill_res(uint8 *res); //Tony23Oct96 + char *GetCdPath( void ); // Chris 9Apr97 + + + mem **resList; //pointer to a pointer (or list of pointers in-fact) + + private: + + int curCd; + uint32 total_res_files; + uint32 total_clusters; + uint32 current_memory_useage; + uint32 resTime; //inc's each time Res_open is called and is given to resource as its age + //cannot be allowed to start at 0! (a pint if you can tell me why) + uint32 *age; + uint16 *res_conv_table; //Gode generated res-id to res number/rel number conversion table + uint16 *count; + char resource_files[MAX_res_files][20]; + uint8 cdTab[MAX_res_files]; // Location of each cluster. + char cdPath[256]; // Drive letter of the CD-ROM drive or false CD path. + void CacheNewCluster(uint32 newCluster); + char cdDrives[24]; +}; + + + +extern resMan res_man; //declare the object global + +#endif diff --git a/sword2/router.cpp b/sword2/router.cpp new file mode 100644 index 0000000000..cf81c45670 --- /dev/null +++ b/sword2/router.cpp @@ -0,0 +1,3089 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//-------------------------------------------------------------------------------------- +// ROUTER.CPP by James + +// A rehash of Jeremy's original jrouter.c, containing low-level system routines +// for calculating routes between points inside a walk-grid, and constructing +// walk animations from mega-sets. + +// jrouter.c undwent 2 major reworks from the original: +// (1) Restructured to allow more flexibility in the mega-sets, ie. more info taken from the walk-data +// - the new George & Nico mega-sets & walk-data were then tested & tweaked in the Sword1 system +// (2) Updated for the new Sword2 system, ie. new object structures +// - now compatible with Sword2, the essential code already having been tested + +//-------------------------------------------------------------------------------------- + + +/**************************************************************************** + * JROUTER.C polygon router with modular walks + * using a tree of modules + * 21 july 94 + * 3 november 94 + * System currently works by scanning grid data and coming up with a ROUTE + * as a series of way points(nodes), the smoothest eight directional PATH + * through these nodes is then found, and a WALK created to fit the PATH. + * + * Two funtions are called by the user, RouteFinder creates a route as a + * module list, HardWalk creates an animation list from the module list. + * The split is only provided to allow the possibility of turning the + * autorouter over two game cycles. + **************************************************************************** + * + * Routine timings on osborne 486 + * + * Read floor resource (file already loaded) 112 pixels + * + * Read mega resource (file already loaded) 112 pixels + * + * + * + **************************************************************************** + * + * Modified 12 Oct 95 + * + * Target Points within 1 pixel of a line are ignored ??? + * + * Modules split into Points within 1 pixel of a line are ignored ??? + * + **************************************************************************** + * + * TOTALLY REHASHED BY JAMES FOR NEW MEGAS USING OLD SYSTEM + * THEN REINCARNATED BY JAMES FOR NEW MEGAS USING NEW SYSTEM + * + **************************************************************************** + ****************************************************************************/ + + +//#define PLOT_PATHS 1 + + +/* + * Include Files + */ + + +#include "driver/driver96.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "interpreter.h" +#include "memory.h" +#include "object.h" +#include "resman.h" +#include "router.h" + +//#ifdef PLOT_PATHS +//#include "grengine.h" +//#endif + +#define MAX_FRAMES_PER_CYCLE 16 +#define NO_DIRECTIONS 8 +#define MAX_FRAMES_PER_CHAR (MAX_FRAMES_PER_CYCLE * NO_DIRECTIONS) +#define ROUTE_END_FLAG 255 + + + + +//--------------------------------------- +// TEMP! +int8 forceSlidy; // 1 = force the use of slidy router (so solid path not used when ending walk in ANY direction) +//--------------------------------------- + +/* + * Type Defines + */ + +#define O_WALKANIM_SIZE 600 // max number of nodes in router output +#define O_GRID_SIZE 200 // max 200 lines & 200 points +#define EXTRA_GRID_SIZE 20 // max 20 lines & 20 points +#define O_ROUTE_SIZE 50 // max number of modules in a route + + +typedef struct +{ + int16 x1; + int16 y1; + int16 x2; + int16 y2; + int16 xmin; + int16 ymin; + int16 xmax; + int16 ymax; + int16 dx; // x2 - x1 + int16 dy; // y2 - y1 + int32 co; // co = (y1 *dx)- (x1*dy) from an equation for a line y*dx = x*dy + co +}_barData; + +typedef struct +{ + int16 x; + int16 y; + int16 level; + int16 prev; + int16 dist; +}_nodeData; + +typedef struct +{ + int32 nbars; + _barData *bars; + int32 nnodes; + _nodeData *node; +} _floorData; + +typedef struct +{ + int32 x; + int32 y; + int32 dirS; + int32 dirD; +} _routeData; + +typedef struct +{ + int32 x; + int32 y; + int32 dir; + int32 num; +} _pathData; + + +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +// Function prototypes + +int32 GetRoute(void); +void ExtractRoute(void); +void LoadWalkGrid(void); +void SetUpWalkGrid(Object_mega *ob_mega, int32 x, int32 y, int32 dir); +void LoadWalkData(Object_walkdata *ob_walkdata); +void PlotCross(int16 x, int16 y, uint8 colour); + +int32 Scan(int32); +int32 NewCheck(int32, int32 , int32 , int32 , int32); +int32 LineCheck(int32 , int32 , int32 , int32); +int32 VertCheck(int32 , int32 , int32); +int32 HorizCheck(int32 , int32 , int32); +int32 Check(int32 , int32 , int32 , int32); +int32 CheckTarget(int32 , int32); + +int32 SmoothestPath(); +int32 SlidyPath(); +int32 SolidPath(); + +int32 SmoothCheck(int32 best, int32 p, int32 dirS, int32 dirD); + +int32 AddSlowInFrames(_walkData *walkAnim); +void AddSlowOutFrames(_walkData *walkAnim); +void SlidyWalkAnimator(_walkData *walkAnim); +int32 SolidWalkAnimator(_walkData *walkAnim); +void RouteLine(int32 x1,int32 y1,int32 x2,int32 y2 ,int32 colour); + +//-------------------------------------------------------------------------------------- +#define MAX_WALKGRIDS 10 + +int32 walkGridList[MAX_WALKGRIDS]; + +//-------------------------------------------------------------------------------------- +#define TOTAL_ROUTE_SLOTS 2 // because we only have 2 megas in the game! + +mem *route_slots[TOTAL_ROUTE_SLOTS]; // stores pointers to mem blocks containing routes created & used by megas (NULL if slot not in use) + +//-------------------------------------------------------------------------------------- +// Local Variables + +static int32 nbars; +static int32 nnodes; +static _barData bars[O_GRID_SIZE+EXTRA_GRID_SIZE]; // because extra bars will be copied into here afer walkgrid loaded +static _nodeData node[O_GRID_SIZE+EXTRA_GRID_SIZE]; + +// area for extra route data to block parts of floors and enable routing round mega charaters +static int32 nExtraBars = 0; +static int32 nExtraNodes = 0; +static _barData extraBars[EXTRA_GRID_SIZE]; +static _nodeData extraNode[EXTRA_GRID_SIZE]; + +static int32 startX; +static int32 startY; +static int32 startDir; +static int32 targetX; +static int32 targetY; +static int32 targetDir; +static int32 scaleA; +static int32 scaleB; +static _routeData route[O_ROUTE_SIZE]; +static _pathData smoothPath[O_ROUTE_SIZE]; +static _pathData modularPath[O_ROUTE_SIZE]; +static int32 routeLength; + + +int32 framesPerStep; +int32 framesPerChar; + +uint8 nWalkFrames; // no. of frames per walk cycle +uint8 usingStandingTurnFrames; // any standing turn frames? +uint8 usingWalkingTurnFrames; // any walking turn frames? +uint8 usingSlowInFrames; // any slow-in frames? +uint8 usingSlowOutFrames; // any slow-out frames? +int32 dx[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR]; +int32 dy[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR]; +int8 modX[NO_DIRECTIONS]; +int8 modY[NO_DIRECTIONS]; +int32 diagonalx = 0; +int32 diagonaly = 0; + +int32 firstStandFrame; + +int32 firstStandingTurnLeftFrame; +int32 firstStandingTurnRightFrame; + +int32 firstWalkingTurnLeftFrame; // left walking turn +int32 firstWalkingTurnRightFrame; // right walking turn + +uint32 firstSlowInFrame[NO_DIRECTIONS]; +uint32 numberOfSlowInFrames[NO_DIRECTIONS]; + +uint32 leadingLeg[NO_DIRECTIONS]; + +int32 firstSlowOutFrame; +int32 numberOfSlowOutFrames; // number of slow-out frames on for each leading-leg in each direction + + +int32 stepCount; + +int32 moduleX; +int32 moduleY; +int32 currentDir; +int32 lastCount; +int32 frame; + +// ie. total number of slow-out frames = (numberOfSlowOutFrames * 2 * NO_DIRECTIONS) + +/* + * CODE + */ + +// ************************************************************************** + +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +uint8 CheckForCollision(void) +{ + static uint32 player_pc; + static uint32 non_player_pc; + + + uint8 collision=0; + + return (collision); +} +//-------------------------------------------------------------------------------------- +uint8 ReturnSlotNo(uint32 megaId) +{ + if (ID==CUR_PLAYER_ID) // George (8) + return(0); + else // One of Nico's mega id's + return(1); +} +//-------------------------------------------------------------------------------------- +void AllocateRouteMem(void) +{ +// uint8 slotNo=0; + uint8 slotNo; + + //------------------------------------------ + // removed (James23June96) + /* + while (route_slots[slotNo] > 0) + { + slotNo++; + + #ifdef _DEBUG + if (slotNo == TOTAL_ROUTE_SLOTS) + Con_fatal_error("ERROR: route_slots[] full in AllocateRouteMem() (%s line %u)",__FILE__,__LINE__); + #endif + } + */ + //------------------------------------------ + // added (James23June96) + // Player character always always slot 0, while the other mega (normally Nico) always uses slot 1 + // Better this way, so that if mega object removed from memory while in middle of route, + // the old route will be safely cleared from memory just before they create a new one + + slotNo = ReturnSlotNo(ID); + + // if this slot is already used, then it can't be needed any more + // because this id is creating a new route! + if (route_slots[slotNo]) + { + FreeRouteMem(); + } + //------------------------------------------ + + route_slots[slotNo] = Twalloc( 4800, MEM_locked, UID_walk_anim ); + // 12000 bytes were used for this in Sword1 mega compacts, based on 20 bytes per '_walkData' frame + // ie. allowing for 600 frames including end-marker + // Now '_walkData' is 8 bytes, so 8*600 = 4800 bytes. + // Note that a 600 frame walk lasts about 48 seconds! (600fps / 12.5s = 48s) + +// megaObject->route_slot_id = slotNo+1; // mega keeps note of which slot contains the pointer to it's walk animation mem block + // +1 so that '0' can mean "not walking" +} +//-------------------------------------------------------------------------------------- +_walkData* LockRouteMem(void) +{ + uint8 slotNo = ReturnSlotNo(ID); + + Lock_mem( route_slots[slotNo] ); + return (_walkData *)route_slots[slotNo]->ad; +} +//-------------------------------------------------------------------------------------- +void FloatRouteMem(void) +{ + uint8 slotNo = ReturnSlotNo(ID); + + Float_mem( route_slots[slotNo] ); +} +//-------------------------------------------------------------------------------------- +void FreeRouteMem(void) +{ + uint8 slotNo = ReturnSlotNo(ID); + + Free_mem( route_slots[slotNo] ); // free the mem block pointed to from this entry of route_slots[] + route_slots[slotNo] = NULL; // clear this route_slots[] entry +} +//-------------------------------------------------------------------------------------- +void FreeAllRouteMem(void) +{ + uint8 slotNo; + + for (slotNo=0; slotNo < TOTAL_ROUTE_SLOTS; slotNo++) + { + if (route_slots[slotNo]) + { + Free_mem( route_slots[slotNo] ); // free the mem block pointed to from this entry of route_slots[] + route_slots[slotNo] = NULL; + } + } +} +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- + +// ************************************************************************** +// ************************************************************************** +// ************************************************************************** +// ************************************************************************** + +int32 RouteFinder(Object_mega *ob_mega, Object_walkdata *ob_walkdata, int32 x, int32 y, int32 dir) +{ +/**************************************************************************** + * RouteFinder.C polygon router with modular walks + * 21 august 94 + * 3 november 94 + * RouteFinder creates a list of modules that enables HardWalk to create + * an animation list. + * + * RouteFinder currently works by scanning grid data and coming up with a ROUTE + * as a series of way points(nodes), the smoothest eight directional PATH + * through these nodes is then found, this information is made available to + * HardWalk for a WALK to be created to fit the PATH. + * + * 30 november 94 return values modified + * + * return 0 = failed to find a route + * + * 1 = found a route + * + * 2 = mega already at target + * + ****************************************************************************/ + + int32 routeFlag = 0; + int32 solidFlag = 0; + _walkData *walkAnim; + +// megaId = id; + + + SetUpWalkGrid(ob_mega, x, y, dir); + LoadWalkData(ob_walkdata); + + walkAnim = LockRouteMem(); // lock the _walkData array (NB. AFTER loading walkgrid & walkdata!) + +// ************************************************************************** +// All route data now loaded start finding a route +// ************************************************************************** +// ************************************************************************** +// Check if we can get a route through the floor changed 12 Oct95 JPS +// ************************************************************************** + + routeFlag = GetRoute(); + + + if (routeFlag == 2) //special case for zero length route + { + if (targetDir >7)// if target direction specified as any + { + targetDir = startDir; + } + // just a turn on the spot is required set an end module for the route let the animator deal with it + // modularPath is normally set by ExtractRoute + modularPath[0].dir = startDir; + modularPath[0].num = 0; + modularPath[0].x = startX; + modularPath[0].y = startY; + modularPath[1].dir = targetDir; + modularPath[1].num = 0; + modularPath[1].x = startX; + modularPath[1].y = startY; + modularPath[2].dir = 9; + modularPath[2].num = ROUTE_END_FLAG; + + SlidyWalkAnimator(walkAnim); + routeFlag = 2; + } + else if (routeFlag == 1) // a normal route + { + SmoothestPath();//Converts the route to an exact path + // The Route had waypoints and direction options + // The Path is an exact set of lines in 8 directions that reach the target. + // The path is in module format, but steps taken in each direction are not accurate + // if target dir = 8 then the walk isn't linked to an anim so + // we can create a route without sliding and miss the exact target + + if (!forceSlidy) + { + if (targetDir == 8) // can end facing ANY direction (ie. exact end position not vital) - so use SOLID walk to avoid sliding to exact position + { + SolidPath(); + solidFlag = SolidWalkAnimator(walkAnim); + } + } + + if(!solidFlag) // if we failed to create a SOLID route, do a SLIDY one instead + { + SlidyPath(); + SlidyWalkAnimator(walkAnim); + } + } + else // Route didn't reach target so assume point was off the floor + { +// routeFlag = 0; + } + + + #ifdef PLOT_PATHS + #ifdef _WIN32 + RenderScreenGDK( screenDef.buffer, scroll_offset_x, scroll_offset_y, screenDef.width * XBLOCKSIZE ); + #else + RenderOffScreenBuffer( scroll_offset_x, scroll_offset_y, SCREEN_WIDTH, SCREEN_DEPTH ); + #endif + FlipScreens(); + + FlushMouseEvents(); // clear mouse buffer + while (!TestForMouseEvent()); // wait for a button press or release + FlushMouseEvents(); // clear mouse buffer again to prevent rapid fire! + #endif + + + FloatRouteMem(); // float the _walkData array again + + return routeFlag; // send back null route +} + +/******************************************************************************* + ******************************************************************************* + * GET A ROUTE + ******************************************************************************* + *******************************************************************************/ + + +int32 GetRoute(void) +{ + /**************************************************************************** + * GetRoute.C extract a path from walk grid + * 12 october 94 + * + * GetRoute currently works by scanning grid data and coming up with a ROUTE + * as a series of way points(nodes). + * static _routeData route[O_ROUTE_SIZE]; + * + * return 0 = failed to find a route + * + * 1 = found a route + * + * 2 = mega already at target + * + * 3 = failed to find a route because target was on a line + * + ****************************************************************************/ + int32 routeGot = 0; + int32 level; + int32 changed; + + if ((startX == targetX) && (startY == targetY)) + routeGot = 2; + + else // 'else' added by JEL (23jan96) otherwise 'routeGot' affected even when already set to '2' above - causing some 'turns' to walk downwards on the spot + routeGot = CheckTarget(targetX,targetY);// returns 3 if target on a line ( +- 1 pixel ) + + + if (routeGot == 0) //still looking for a route check if target is within a pixel of a line + { + // scan through the nodes linking each node to its nearest neighbour until no more nodes change + // This is the routine that finds a route using Scan() + level = 1; + do + { + changed = Scan(level); + level =level + 1; + } + while(changed == 1); + + // Check to see if the route reached the target + if (node[nnodes].dist < 9999) + { + routeGot = 1; + ExtractRoute(); // it did so extract the route as nodes and the directions to go between each node + // route.X,route.Y and route.Dir now hold all the route infomation with the target dir or route continuation + } + } + + return routeGot; +} + + +/******************************************************************************* + ******************************************************************************* + * THE SLIDY PATH ROUTINES + ******************************************************************************* + *******************************************************************************/ + + +int32 SmoothestPath() +{ +/* + * This is the second big part of the route finder and the the only bit that tries to be clever + * (the other bits are clever). + * This part of the autorouter creates a list of modules from a set of lines running across the screen + * The task is complicated by two things; + * Firstly in choosing a route through the maze of nodes the routine tries to minimise the amount of each + * individual turn avoiding 90 degree and greater turns (where possible) and reduces the total number of + * turns (subject to two 45 degree turns being better than one 90 degree turn). + * Secondly when walking in a given direction the number of steps required to reach the end of that run + * is not calculated accurately. This is because I was unable to derive a function to relate number of + * steps taken between two points to the shrunken step size + * + */ + int32 p; + int32 dirS; + int32 dirD; + int32 dS; + int32 dD; + int32 dSS; + int32 dSD; + int32 dDS; + int32 dDD; + int32 SS; + int32 SD; + int32 DS; + int32 DD; + int32 i; + int32 j; + int32 temp; + int32 steps; + int32 option; + int32 options; + int32 lastDir; + int32 nextDirS; + int32 nextDirD; + int32 tempturns[4]; + int32 turns[4]; + int32 turntable[NO_DIRECTIONS] = {0,1,3,5,7,5,3,1}; + + // route.X route.Y and route.Dir start at far end + smoothPath[0].x = startX; + smoothPath[0].y = startY; + smoothPath[0].dir = startDir; + smoothPath[0].num = 0; + p = 0; + lastDir = startDir; + // for each section of the route + do + { + + dirS = route[p].dirS; + dirD = route[p].dirD; + nextDirS = route[p+1].dirS; + nextDirD = route[p+1].dirD; + + // Check directions into and out of a pair of nodes + // going in + dS = dirS - lastDir; + if ( dS < 0) + dS = dS + NO_DIRECTIONS; + + dD = dirD - lastDir; + if ( dD < 0) + dD = dD + NO_DIRECTIONS; + + // coming out + dSS = dirS - nextDirS; + if ( dSS < 0) + dSS = dSS + NO_DIRECTIONS; + + dDD = dirD - nextDirD; + if ( dDD < 0) + dDD = dDD + NO_DIRECTIONS; + + dSD = dirS - nextDirD; + if ( dSD < 0) + dSD = dSD + NO_DIRECTIONS; + + dDS = dirD - nextDirS; + if ( dDS < 0) + dDS = dDS + NO_DIRECTIONS; + + // Determine the amount of turning involved in each possible path + dS = turntable[dS]; + dD = turntable[dD]; + dSS = turntable[dSS]; + dDD = turntable[dDD]; + dSD = turntable[dSD]; + dDS = turntable[dDS]; + // get the best path out ie assume next section uses best direction + if (dSD < dSS) + { + dSS = dSD; + } + if (dDS < dDD) + { + dDD = dDS; + } + // rate each option + SS = dS + dSS + 3; // Split routes look crap so weight against them + SD = dS + dDD; + DS = dD + dSS; + DD = dD + dDD + 3; + // set up turns as a sorted array of the turn values + tempturns[0] = SS; + turns[0] = 0; + tempturns[1] = SD; + turns[1] = 1; + tempturns[2] = DS; + turns[2] = 2; + tempturns[3] = DD; + turns[3] = 3; + i = 0; + do + { + j = 0; + do + { + if (tempturns[j] > tempturns[j + 1]) + { + temp = turns[j]; + turns[j] = turns[j+1]; + turns[j+1] = temp; + temp = tempturns[j]; + tempturns[j] = tempturns[j+1]; + tempturns[j+1] = temp; + } + j = j + 1; + } + while (j < 3); + i = i + 1; + } + while (i < 3); + + // best option matched in order of the priority we would like to see on the screen + // but each option must be checked to see if it can be walked + + options = NewCheck(1, route[p].x, route[p].y, route[p + 1].x, route[p + 1].y); + +#ifdef _DEBUG + if (options == 0) + { + Zdebug("BestTurns fail %d %d %d %d",route[p].x, route[p].y, route[p + 1].x, route[p + 1].y); + Zdebug("BestTurns fail %d %d %d %d",turns[0],turns[1],turns[2],options); + Con_fatal_error("BestTurns failed (%s line %u)",__FILE__,__LINE__); + } +#endif + + i = 0; + steps = 0; + do + { + option = 1 << turns[i]; + if (option & options) + steps = SmoothCheck(turns[i],p,dirS,dirD); + i = i + 1; + } + while ((steps == 0) && (i < 4)); + +#ifdef PLOT_PATHS // plot the best path + if (steps != 0) + { + i = 0; + do + { + RouteLine(smoothPath[i].x, smoothPath[i].y, smoothPath[i+1].x, smoothPath[i+1].y, 228); + i = i + 1; + } + while (i < steps); + } +#endif + + +#ifdef _DEBUG + if (steps == 0) + { + Zdebug("BestTurns failed %d %d %d %d",route[p].x, route[p].y, route[p + 1].x, route[p + 1].y); + Zdebug("BestTurns failed %d %d %d %d",turns[0],turns[1],turns[2],options); + Con_fatal_error("BestTurns failed (%s line %u)",__FILE__,__LINE__); + } +#endif + + // route.X route.Y route.dir and bestTurns start at far end + p = p + 1; + + + } + while (p < (routeLength)); + // best turns will end heading as near as possible to target dir rest is down to anim for now + smoothPath[steps].dir = 9; + smoothPath[steps].num = ROUTE_END_FLAG; + return 1; +} + + + + +int32 SmoothCheck(int32 best, int32 p, int32 dirS, int32 dirD) +/**************************************************************************** + * Slip sliding away + * This path checker checks to see if a walk that exactly follows the path + * would be valid. This should be inherently true for atleast one of the turn + * options. + * No longer checks the data it only creates the smoothPath array JPS + ****************************************************************************/ +{ + static int32 k; + int32 tempK; + int32 x; + int32 y; + int32 x2; + int32 y2; + int32 dx; + int32 dy; + int32 dsx; + int32 dsy; + int32 ddx; + int32 ddy; + int32 dirX; + int32 dirY; + int32 ss0; + int32 ss1; + int32 ss2; + int32 sd0; + int32 sd1; + int32 sd2; + + + if (p == 0) + { + k = 1; + } + tempK = 0; + x = route[p].x; + y = route[p].y; + x2 = route[p + 1].x; + y2 = route[p + 1].y; + dx = x2 - x; + dy = y2 - y; + dirX = 1; + dirY = 1; + if (dx < 0) + { + dx = -dx; + dirX = -1; + } + + if (dy < 0) + { + dy = -dy; + dirY = -1; + } + +// set up sd0-ss2 to reflect possible movement in each direction + if ((dirS == 0) || (dirS == 4))// vert and diag + { + ddx = dx; + ddy = (dx*diagonaly)/diagonalx; + dsy = dy - ddy; + ddx = ddx * dirX; + ddy = ddy * dirY; + dsy = dsy * dirY; + dsx = 0; + + sd0 = (ddx + modX[dirD]/2)/ modX[dirD]; + ss0 = (dsy + modY[dirS]/2) / modY[dirS]; + sd1 = sd0/2; + ss1 = ss0/2; + sd2 = sd0 - sd1; + ss2 = ss0 - ss1; + } + else + { + ddy = dy; + ddx = (dy*diagonalx)/diagonaly; + dsx = dx - ddx; + ddy = ddy * dirY; + ddx = ddx * dirX; + dsx = dsx * dirX; + dsy = 0; + + sd0 = (ddy + modY[dirD]/2)/ modY[dirD]; + ss0 = (dsx + modX[dirS]/2)/ modX[dirS]; + sd1 = sd0/2; + ss1 = ss0/2; + sd2 = sd0 - sd1; + ss2 = ss0 - ss1; + } + + if (best == 0) //halfsquare, diagonal, halfsquare + { + smoothPath[k].x = x+dsx/2; + smoothPath[k].y = y+dsy/2; + smoothPath[k].dir = dirS; + smoothPath[k].num = ss1; + k = k + 1; + smoothPath[k].x = x+dsx/2+ddx; + smoothPath[k].y = y+dsy/2+ddy; + smoothPath[k].dir = dirD; + smoothPath[k].num = sd0; + k = k + 1; + smoothPath[k].x = x+dsx+ddx; + smoothPath[k].y = y+dsy+ddy; + smoothPath[k].dir = dirS; + smoothPath[k].num = ss2; + k = k + 1; + tempK = k; + } + else if (best == 1) //square, diagonal + { + smoothPath[k].x = x+dsx; + smoothPath[k].y = y+dsy; + smoothPath[k].dir = dirS; + smoothPath[k].num = ss0; + k = k + 1; + smoothPath[k].x = x2; + smoothPath[k].y = y2; + smoothPath[k].dir = dirD; + smoothPath[k].num = sd0; + k = k + 1; + tempK = k; + } + else if (best == 2) //diagonal square + { + smoothPath[k].x = x+ddx; + smoothPath[k].y = y+ddy; + smoothPath[k].dir = dirD; + smoothPath[k].num = sd0; + k = k + 1; + smoothPath[k].x = x2; + smoothPath[k].y = y2; + smoothPath[k].dir = dirS; + smoothPath[k].num = ss0; + k = k + 1; + tempK = k; + } + else //halfdiagonal, square, halfdiagonal + { + smoothPath[k].x = x+ddx/2; + smoothPath[k].y = y+ddy/2; + smoothPath[k].dir = dirD; + smoothPath[k].num = sd1; + k = k + 1; + smoothPath[k].x = x+dsx+ddx/2; + smoothPath[k].y = y+dsy+ddy/2; + smoothPath[k].dir = dirS; + smoothPath[k].num = ss0; + k = k + 1; + smoothPath[k].x = x2; + smoothPath[k].y = y2; + smoothPath[k].dir = dirD; + smoothPath[k].num = sd2; + k = k + 1; + tempK = k; + } + + return tempK; +} + +int32 SlidyPath() +{ +/**************************************************************************** + * SlidyPath creates a path based on part steps with no sliding to get + * as near as possible to the target without any sliding this routine is + * currently unused, but is intended for use when just clicking about. + * + * produce a module list from the line data + * + ****************************************************************************/ +int32 smooth; +int32 slidy; +int32 scale; +int32 stepX; +int32 stepY; +int32 deltaX; +int32 deltaY; + + // strip out the short sections + slidy = 1; + smooth = 1; + modularPath[0].x = smoothPath[0].x; + modularPath[0].y = smoothPath[0].y; + modularPath[0].dir = smoothPath[0].dir; + modularPath[0].num = 0; + + while (smoothPath[smooth].num < ROUTE_END_FLAG) + { + scale = scaleA * smoothPath[smooth].y + scaleB; + deltaX = smoothPath[smooth].x - modularPath[slidy-1].x; + deltaY = smoothPath[smooth].y - modularPath[slidy-1].y; + stepX = modX[smoothPath[smooth].dir]; + stepY = modY[smoothPath[smooth].dir]; + stepX = stepX * scale; + stepY = stepY * scale; + stepX = stepX >> 19;// quarter a step minimum + stepY = stepY >> 19; + if ((abs(deltaX)>=abs(stepX)) && (abs(deltaY)>=abs(stepY))) + { + modularPath[slidy].x = smoothPath[smooth].x; + modularPath[slidy].y = smoothPath[smooth].y; + modularPath[slidy].dir = smoothPath[smooth].dir; + modularPath[slidy].num = 1; + slidy += 1; + } + smooth += 1; + } + // in case the last bit had no steps + if (slidy > 1) + { + modularPath[slidy-1].x = smoothPath[smooth-1].x; + modularPath[slidy-1].y = smoothPath[smooth-1].y; + } + // set up the end of the walk + modularPath[slidy].x = smoothPath[smooth-1].x; + modularPath[slidy].y = smoothPath[smooth-1].y; + modularPath[slidy].dir = targetDir; + modularPath[slidy].num = 0; + slidy += 1; + modularPath[slidy].x = smoothPath[smooth-1].x; + modularPath[slidy].y = smoothPath[smooth-1].y; + modularPath[slidy].dir = 9; + modularPath[slidy].num = ROUTE_END_FLAG; + return 1; + +} + +//**************************************************************************** +// SLOW IN + +int32 AddSlowInFrames(_walkData *walkAnim) +{ + uint32 slowInFrameNo; + + + if ((usingSlowInFrames) && (modularPath[1].num > 0)) + { + for (slowInFrameNo=0; slowInFrameNo<numberOfSlowInFrames[currentDir]; slowInFrameNo++) + { + walkAnim[stepCount].frame = firstSlowInFrame[currentDir] + slowInFrameNo; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = currentDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + return(1); + } + else + { + return(0); + } +} +//---------------------------------------------------------------------------- +void EarlySlowOut(Object_mega *ob_mega, Object_walkdata *ob_walkdata) +{ + int32 slowOutFrameNo; + int32 walk_pc; + _walkData *walkAnim; + + + //Zdebug("\nEARLY SLOW-OUT"); + + LoadWalkData(ob_walkdata); + + //Zdebug("********************************"); + //Zdebug("framesPerStep =%d",framesPerStep); // 6; + //Zdebug("numberOfSlowOutFrames =%d",numberOfSlowOutFrames); // 7; + //Zdebug("firstWalkingTurnLeftFrame =%d",firstWalkingTurnLeftFrame); // 120; + //Zdebug("firstWalkingTurnRightFrame =%d",firstWalkingTurnRightFrame); // 216; + //Zdebug("firstSlowOutFrame =%d",firstSlowOutFrame); // 344; + //Zdebug("********************************"); + + + walk_pc = ob_mega->walk_pc; + + walkAnim = LockRouteMem(); // lock the _walkData array (NB. AFTER loading walkgrid & walkdata!) + + + if (usingSlowOutFrames) // if this mega does actually have slow-out frames + { + do // overwrite the next step (half a cycle) of the walk (ie .step - 0..5 + { + //Zdebug("\nSTEP NUMBER: walkAnim[%d].step = %d",walk_pc,walkAnim[walk_pc].step); + //Zdebug("ORIGINAL FRAME: walkAnim[%d].frame = %d",walk_pc,walkAnim[walk_pc].frame); + // map from existing walk frame across to correct frame number of slow-out - remember, there may be more slow-out frames than walk-frames! + + if (walkAnim[walk_pc].frame >= firstWalkingTurnRightFrame) // if it's a walking turn-right, rather than a normal step + { + walkAnim[walk_pc].frame -= firstWalkingTurnRightFrame; // then map it to a normal step frame first + //Zdebug("MAPPED TO WALK: walkAnim[%d].frame = %d (walking turn-right frame --> walk frame)",walk_pc,walkAnim[walk_pc].frame); + } + + else if (walkAnim[walk_pc].frame >= firstWalkingTurnLeftFrame) // if it's a walking turn-left, rather than a normal step + { + walkAnim[walk_pc].frame -= firstWalkingTurnLeftFrame; // then map it to a normal step frame first + //Zdebug("MAPPED TO WALK: walkAnim[%d].frame = %d (walking turn-left frame --> walk frame)",walk_pc,walkAnim[walk_pc].frame); + } + + walkAnim[walk_pc].frame += firstSlowOutFrame + ((walkAnim[walk_pc].frame / framesPerStep) * (numberOfSlowOutFrames-framesPerStep)); + walkAnim[walk_pc].step = 0; + //Zdebug("SLOW-OUT FRAME: walkAnim[%d].frame = %d",walk_pc,walkAnim[walk_pc].frame); + walk_pc += 1; + } + while(walkAnim[walk_pc].step > 0 ); + + //Zdebug("\n"); + + for (slowOutFrameNo=framesPerStep; slowOutFrameNo < numberOfSlowOutFrames; slowOutFrameNo++) // add stationary frame(s) (OPTIONAL) + { + walkAnim[walk_pc].frame = walkAnim[walk_pc-1].frame + 1; + //Zdebug("EXTRA FRAME: walkAnim[%d].frame = %d",walk_pc,walkAnim[walk_pc].frame); + walkAnim[walk_pc].step = 0; + walkAnim[walk_pc].dir = walkAnim[walk_pc-1].dir; + walkAnim[walk_pc].x = walkAnim[walk_pc-1].x; + walkAnim[walk_pc].y = walkAnim[walk_pc-1].y; + walk_pc++; + } + } + else // this mega doesn't have slow-out frames + { + walkAnim[walk_pc].frame = firstStandFrame + walkAnim[walk_pc-1].dir; // stand in current direction + walkAnim[walk_pc].step = 0; + walkAnim[walk_pc].dir = walkAnim[walk_pc-1].dir; + walkAnim[walk_pc].x = walkAnim[walk_pc-1].x; + walkAnim[walk_pc].y = walkAnim[walk_pc-1].y; + walk_pc++; + } + + walkAnim[walk_pc].frame = 512; // end of sequence + walkAnim[walk_pc].step = 99; // so that this doesn't happen again while 'george_walking' is still '2' +} +//---------------------------------------------------------------------------- +// SLOW OUT + +void AddSlowOutFrames(_walkData *walkAnim) +{ + int32 slowOutFrameNo; + + + if ((usingSlowOutFrames)&&(lastCount>=framesPerStep)) // if the mega did actually walk, we overwrite the last step (half a cycle) with slow-out frames + add any necessary stationary frames + { + // place stop frames here + // slowdown at the end of the last walk + + slowOutFrameNo = lastCount - framesPerStep; + + + //Zdebug("SLOW OUT: slowOutFrameNo(%d) = lastCount(%d) - framesPerStep(%d)",slowOutFrameNo,lastCount,framesPerStep); + + do // overwrite the last step (half a cycle) of the walk + { + // map from existing walk frame across to correct frame number of slow-out - remember, there may be more slow-out frames than walk-frames! + walkAnim[slowOutFrameNo].frame += firstSlowOutFrame + ((walkAnim[slowOutFrameNo].frame / framesPerStep) * (numberOfSlowOutFrames-framesPerStep)); + walkAnim[slowOutFrameNo].step = 0; // because no longer a normal walk-step + //Zdebug("walkAnim[%d].frame = %d",slowOutFrameNo,walkAnim[slowOutFrameNo].frame); + slowOutFrameNo += 1; + } + while(slowOutFrameNo < lastCount ); + + for (slowOutFrameNo=framesPerStep; slowOutFrameNo < numberOfSlowOutFrames; slowOutFrameNo++) // add stationary frame(s) (OPTIONAL) + { + walkAnim[stepCount].frame = walkAnim[stepCount-1].frame + 1; + //Zdebug("EXTRA FRAMES: walkAnim[%d].frame = %d",stepCount,walkAnim[stepCount].frame); + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = walkAnim[stepCount-1].dir; + walkAnim[stepCount].x = walkAnim[stepCount-1].x; + walkAnim[stepCount].y = walkAnim[stepCount-1].y; + stepCount += 1; + } + } +} +//---------------------------------------------------------------------------- + +void SlidyWalkAnimator(_walkData *walkAnim) +/**************************************************************************** + * Skidding every where HardWalk creates an animation that exactly fits the + * smoothPath and uses foot slipping to fit whole steps into the route + * Parameters: georgeg,mouseg + * Returns: rout + * + * produce a module list from the line data + * + ****************************************************************************/ +{ + static int32 left = 0; + int32 p; + int32 lastDir; + int32 lastRealDir; + int32 turnDir; + int32 scale; + int32 step; + int32 module; + int32 moduleEnd; + int32 module16X; + int32 module16Y; + int32 stepX; + int32 stepY; + int32 errorX; + int32 errorY; + int32 lastErrorX; + int32 lastErrorY; + int32 frameCount; + int32 frames; + + + p = 0; + lastDir = modularPath[0].dir; + currentDir = modularPath[1].dir; + if (currentDir == NO_DIRECTIONS) + { + currentDir = lastDir; + } + moduleX = startX; + moduleY = startY; + module16X = moduleX << 16; + module16Y = moduleY << 16; + stepCount = 0; + + //**************************************************************************** + // SLIDY + // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY + // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED + //**************************************************************************** + //Zdebug("\nSLIDY: STARTING THE WALK"); + module = framesPerChar + lastDir; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + + //**************************************************************************** + // SLIDY + // TURN TO START THE WALK + //**************************************************************************** + //Zdebug("\nSLIDY: TURNING TO START THE WALK"); + // rotate if we need to + if (lastDir != currentDir) + { + // get the direction to turn + turnDir = currentDir - lastDir; + if ( turnDir < 0) + turnDir += NO_DIRECTIONS; + + if (turnDir > 4) + turnDir = -1; + else if (turnDir > 0) + turnDir = 1; + + // rotate to new walk direction + // for george and nico put in a head turn at the start + if (usingStandingTurnFrames) + { + if ( turnDir < 0) // new frames for turn frames 29oct95jps + { + module = firstStandingTurnLeftFrame + lastDir; + } + else + { + module = firstStandingTurnRightFrame + lastDir; + } + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + + // rotate till were facing new dir then go back 45 degrees + while (lastDir != currentDir) + { + lastDir += turnDir; + if ( turnDir < 0) // new frames for turn frames 29oct95jps + { + if ( lastDir < 0) + lastDir += NO_DIRECTIONS; + module = firstStandingTurnLeftFrame + lastDir; + } + else + { + if ( lastDir > 7) + lastDir -= NO_DIRECTIONS; + module = firstStandingTurnRightFrame + lastDir; + } + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + // the back 45 degrees bit + stepCount -= 1;// step back one because new head turn for george takes us past the new dir + } + // his head is in the right direction + lastRealDir = currentDir; + + //**************************************************************************** + // SLIDY: THE SLOW IN + + AddSlowInFrames(walkAnim); + //**************************************************************************** + + //**************************************************************************** + // SLIDY + // THE WALK + //**************************************************************************** + + //Zdebug("\nSLIDY: THE WALK"); + + //--------------------------------------------------- + // start the walk on the left or right leg, depending on how the slow-in frames were drawn + + if (leadingLeg[currentDir]==0) // (0=left; 1=right) + left = 0; // start the walk on the left leg (ie. at beginning of the first step of the walk cycle) + else + left = framesPerStep; // start the walk on the right leg (ie. at beginning of the second step of the walk cycle) + //--------------------------------------------------- + + + lastCount = stepCount; + lastDir = 99;// this ensures that we don't put in turn frames for the start + currentDir = 99;// this ensures that we don't put in turn frames for the start + do + { + while (modularPath[p].num == 0) + { + p = p + 1; + if (currentDir != 99) + lastRealDir = currentDir; + lastDir = currentDir; + lastCount = stepCount; + } + //calculate average amount to lose in each step on the way to the next node + currentDir = modularPath[p].dir; + if (currentDir < NO_DIRECTIONS) + { + module = currentDir * framesPerStep * 2 + left; + if (left == 0) + left = framesPerStep; + else + left = 0; + moduleEnd = module + framesPerStep; + step = 0; + scale = (scaleA * moduleY + scaleB); + do + { + module16X += dx[module]*scale; + module16Y += dy[module]*scale; + moduleX = module16X >> 16; + moduleY = module16Y >> 16; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = step; // normally 0,1,2,3,4,5,0,1,2,etc + walkAnim[stepCount].dir = currentDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + step += 1; + module += 1; + } + while( module < moduleEnd) ; + stepX = modX[modularPath[p].dir]; + stepY = modY[modularPath[p].dir]; + errorX = modularPath[p].x - moduleX; + errorX = errorX * stepX; + errorY = modularPath[p].y - moduleY; + errorY = errorY * stepY; + if ((errorX < 0) || (errorY < 0)) + { + modularPath[p].num = 0; // the end of the path + // okay those last steps took us past our target but do we want to scoot or moonwalk + frames = stepCount - lastCount; + errorX = modularPath[p].x - walkAnim[stepCount-1].x; + errorY = modularPath[p].y - walkAnim[stepCount-1].y; + + if (frames > framesPerStep) + { + lastErrorX = modularPath[p].x - walkAnim[stepCount-7].x; + lastErrorY = modularPath[p].y - walkAnim[stepCount-7].y; + if (stepX==0) + { + if (3*abs(lastErrorY) < abs(errorY)) //the last stop was closest + { + stepCount -= framesPerStep; + if (left == 0) + left = framesPerStep; + else + left = 0; + } + } + else + { + if (3*abs(lastErrorX) < abs(errorX)) //the last stop was closest + { + stepCount -= framesPerStep; + if (left == 0) + left = framesPerStep; + else + left = 0; + } + } + } + errorX = modularPath[p].x - walkAnim[stepCount-1].x; + errorY = modularPath[p].y - walkAnim[stepCount-1].y; + // okay we've reached the end but we still have an error + if (errorX != 0) + { + frameCount = 0; + frames = stepCount - lastCount; + do + { + frameCount += 1; + walkAnim[lastCount + frameCount - 1].x += errorX*frameCount/frames; + } + while(frameCount<frames); + } + if (errorY != 0) + { + frameCount = 0; + frames = stepCount - lastCount; + do + { + frameCount += 1; + walkAnim[lastCount + frameCount-1].y += errorY*frameCount/frames; + } + while(frameCount<frames); + } + // Now is the time to put in the turn frames for the last turn + if (frames < framesPerStep) + currentDir = 99;// this ensures that we don't put in turn frames for this walk or the next + if (currentDir != 99) + lastRealDir = currentDir; + // check each turn condition in turn + if (((lastDir != 99) && (currentDir != 99)) && (usingWalkingTurnFrames)) // only for george + { + lastDir = currentDir - lastDir;//1 and -7 going right -1 and 7 going left + if (((lastDir == -1) || (lastDir == 7)) || ((lastDir == -2) || (lastDir == 6))) + { + // turn at the end of the last walk + frame = lastCount - framesPerStep; + do + { + walkAnim[frame].frame += firstWalkingTurnLeftFrame; //was 104; //turning left + frame += 1; + } + while(frame < lastCount ); + } + if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6))) + { + // turn at the end of the current walk + frame = lastCount - framesPerStep; + do + { + walkAnim[frame].frame += firstWalkingTurnRightFrame; // was 200; // turning right + frame += 1; + } + while(frame < lastCount ); + } + lastDir = currentDir; + } + // all turns checked + + lastCount = stepCount; + moduleX = walkAnim[stepCount-1].x; + moduleY = walkAnim[stepCount-1].y; + module16X = moduleX << 16; + module16Y = moduleY << 16; + } + } + } + while (modularPath[p].dir < NO_DIRECTIONS); + + + +#ifdef _DEBUG + if (lastRealDir == 99) + { + Con_fatal_error("SlidyWalkAnimatorlast direction error (%s line %u)",__FILE__,__LINE__); + } +#endif + + //**************************************************************************** + // SLIDY: THE SLOW OUT + AddSlowOutFrames(walkAnim); + + //**************************************************************************** + // SLIDY + // TURNS TO END THE WALK ? + //**************************************************************************** + + // We've done the walk now put in any turns at the end + + + if (targetDir == 8) // ANY direction -> stand in the last direction + { + module = firstStandFrame + lastRealDir; + targetDir = lastRealDir; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastRealDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + if (targetDir == 9) // 'stance' was non-zero + { + if (stepCount == 0) + { + module = framesPerChar + lastRealDir; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastRealDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + } + else if (targetDir != lastRealDir) // rotate to targetDir + { + // rotate to target direction + turnDir = targetDir - lastRealDir; + if ( turnDir < 0) + turnDir += NO_DIRECTIONS; + + if (turnDir > 4) + turnDir = -1; + else if (turnDir > 0) + turnDir = 1; + + // rotate to target direction + // for george and nico put in a head turn at the start + if (usingStandingTurnFrames) + { + if ( turnDir < 0) // new frames for turn frames 29oct95jps + { + module = firstStandingTurnLeftFrame + lastDir; + } + else + { + module = firstStandingTurnRightFrame + lastDir; + } + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastRealDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + + // rotate if we need to + while (lastRealDir != targetDir) + { + lastRealDir += turnDir; + if ( turnDir < 0) // new frames for turn frames 29oct95jps + { + if ( lastRealDir < 0) + lastRealDir += NO_DIRECTIONS; + module = firstStandingTurnLeftFrame + lastRealDir; + } + else + { + if ( lastRealDir > 7) + lastRealDir -= NO_DIRECTIONS; + module = firstStandingTurnRightFrame + lastRealDir; + } + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastRealDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + module = firstStandFrame + lastRealDir; + walkAnim[stepCount-1].frame = module; + } + else // just stand at the end + { + module = firstStandFrame + lastRealDir; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastRealDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + + walkAnim[stepCount].frame = 512; + walkAnim[stepCount].step = 99; + stepCount += 1; + walkAnim[stepCount].frame = 512; + walkAnim[stepCount].step = 99; + stepCount += 1; + walkAnim[stepCount].frame = 512; + walkAnim[stepCount].step = 99; + + + //------------------------------------------- + // write all the frames to "debug.txt" + + //Zdebug("\nTHE WALKDATA:"); + for (frame=0; frame<=stepCount; frame++) + { + //Zdebug("walkAnim[%d].frame=%d",frame,walkAnim[frame].frame); + } + //------------------------------------------- + + +// Zdebug("RouteFinder RouteSize is %d", stepCount); + return; +} + + +/******************************************************************************* + ******************************************************************************* + * THE SOLID PATH ROUTINES + ******************************************************************************* + *******************************************************************************/ + +int32 SolidPath() +{ +/**************************************************************************** + * SolidPath creates a path based on whole steps with no sliding to get + * as near as possible to the target without any sliding this routine is + * currently unused, but is intended for use when just clicking about. + * + * produce a module list from the line data + * + ****************************************************************************/ +int32 smooth; +int32 solid; +int32 scale; +int32 stepX; +int32 stepY; +int32 deltaX; +int32 deltaY; + + // strip out the short sections + solid = 1; + smooth = 1; + modularPath[0].x = smoothPath[0].x; + modularPath[0].y = smoothPath[0].y; + modularPath[0].dir = smoothPath[0].dir; + modularPath[0].num = 0; + + do + { + scale = scaleA * smoothPath[smooth].y + scaleB; + deltaX = smoothPath[smooth].x - modularPath[solid-1].x; + deltaY = smoothPath[smooth].y - modularPath[solid-1].y; + stepX = modX[smoothPath[smooth].dir]; + stepY = modY[smoothPath[smooth].dir]; + stepX = stepX * scale; + stepY = stepY * scale; + stepX = stepX >> 16; + stepY = stepY >> 16; + if ((abs(deltaX)>=abs(stepX)) && (abs(deltaY)>=abs(stepY))) + { + modularPath[solid].x = smoothPath[smooth].x; + modularPath[solid].y = smoothPath[smooth].y; + modularPath[solid].dir = smoothPath[smooth].dir; + modularPath[solid].num = 1; + solid += 1; + } + smooth += 1; + } + while (smoothPath[smooth].num < ROUTE_END_FLAG); + // in case the last bit had no steps + if (solid == 1) //there were no paths so put in a dummy end + { + solid = 2; + modularPath[1].dir = smoothPath[0].dir; + modularPath[1].num = 0; + } + modularPath[solid-1].x = smoothPath[smooth-1].x; + modularPath[solid-1].y = smoothPath[smooth-1].y; + // set up the end of the walk + modularPath[solid].x = smoothPath[smooth-1].x; + modularPath[solid].y = smoothPath[smooth-1].y; + modularPath[solid].dir = 9; + modularPath[solid].num = ROUTE_END_FLAG; + return 1; + +} + +int32 SolidWalkAnimator(_walkData *walkAnim) +{ +/**************************************************************************** + * SolidWalk creates an animation based on whole steps with no sliding to get + * as near as possible to the target without any sliding this routine is + * is intended for use when just clicking about. + * + * produce a module list from the line data + * + * returns 0 if solid route not found + ****************************************************************************/ + + int32 p; + int32 i; + int32 left; + int32 lastDir; + int32 turnDir; + int32 scale; + int32 step; + int32 module; + int32 module16X; + int32 module16Y; + int32 errorX; + int32 errorY; + int32 moduleEnd; + int32 slowStart=0; + + + + // start at the beginning for a change + lastDir = modularPath[0].dir; + p = 1; + currentDir = modularPath[1].dir; + module = framesPerChar + lastDir; + moduleX = startX; + moduleY = startY; + module16X = moduleX << 16; + module16Y = moduleY << 16; + stepCount = 0; + + //**************************************************************************** + // SOLID + // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY + // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED + //**************************************************************************** + + //Zdebug("\nSOLID: STARTING THE WALK"); + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + + //**************************************************************************** + // SOLID + // TURN TO START THE WALK + //**************************************************************************** + //Zdebug("\nSOLID: TURNING TO START THE WALK"); + // rotate if we need to + if (lastDir != currentDir) + { + // get the direction to turn + turnDir = currentDir - lastDir; + if ( turnDir < 0) + turnDir += NO_DIRECTIONS; + + if (turnDir > 4) + turnDir = -1; + else if (turnDir > 0) + turnDir = 1; + + // rotate to new walk direction + // for george and nico put in a head turn at the start + if (usingStandingTurnFrames) + { + if ( turnDir < 0) // new frames for turn frames 29oct95jps + { + module = firstStandingTurnLeftFrame + lastDir; + } + else + { + module = firstStandingTurnRightFrame + lastDir; + } + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + + // rotate till were facing new dir then go back 45 degrees + while (lastDir != currentDir) + { + lastDir += turnDir; + if ( turnDir < 0) // new frames for turn frames 29oct95jps + { + if ( lastDir < 0) + lastDir += NO_DIRECTIONS; + module = firstStandingTurnLeftFrame + lastDir; + } + else + { + if ( lastDir > 7) + lastDir -= NO_DIRECTIONS; + module = firstStandingTurnRightFrame + lastDir; + } + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = lastDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + } + // the back 45 degrees bit + stepCount -= 1;// step back one because new head turn for george takes us past the new dir + } + + //**************************************************************************** + // SOLID: THE SLOW IN + + slowStart = AddSlowInFrames(walkAnim); + + //**************************************************************************** + // SOLID + // THE WALK + //**************************************************************************** + + //Zdebug("\nSOLID: THE WALK"); + + //--------------------------------------------------- + // start the walk on the left or right leg, depending on how the slow-in frames were drawn + + if (leadingLeg[currentDir]==0) // (0=left; 1=right) + left = 0; // start the walk on the left leg (ie. at beginning of the first step of the walk cycle) + else + left = framesPerStep; // start the walk on the right leg (ie. at beginning of the second step of the walk cycle) + //--------------------------------------------------- + + + lastCount = stepCount; + lastDir = 99;// this ensures that we don't put in turn frames for the start + currentDir = 99;// this ensures that we don't put in turn frames for the start + + do + { + while(modularPath[p].num > 0) + { + currentDir = modularPath[p].dir; + if (currentDir< NO_DIRECTIONS) + { + + module = currentDir * framesPerStep * 2 + left; + if (left == 0) + left = framesPerStep; + else + left = 0; + moduleEnd = module + framesPerStep; + step = 0; + scale = (scaleA * moduleY + scaleB); + do + { + module16X += dx[module]*scale; + module16Y += dy[module]*scale; + moduleX = module16X >> 16; + moduleY = module16Y >> 16; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = step; // normally 0,1,2,3,4,5,0,1,2,etc + walkAnim[stepCount].dir = currentDir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + module += 1; + step += 1; + } + while( module < moduleEnd) ; + errorX = modularPath[p].x - moduleX; + errorX = errorX * modX[modularPath[p].dir]; + errorY = modularPath[p].y - moduleY; + errorY = errorY * modY[modularPath[p].dir]; + if ((errorX < 0) || (errorY < 0)) + { + modularPath[p].num = 0; + stepCount -= framesPerStep; + if (left == 0) + left = framesPerStep; + else + left = 0; + // Okay this is the end of a section + moduleX = walkAnim[stepCount-1].x; + moduleY = walkAnim[stepCount-1].y; + module16X = moduleX << 16; + module16Y = moduleY << 16; + modularPath[p].x =moduleX; + modularPath[p].y =moduleY; + // Now is the time to put in the turn frames for the last turn + if ((stepCount - lastCount) < framesPerStep)// no step taken + { + if (slowStart == 1)// clean up if a slow in but no walk + { + //stepCount -= 3; + stepCount -= numberOfSlowInFrames[currentDir]; // (James08sep97) + //lastCount -= 3; + lastCount -= numberOfSlowInFrames[currentDir]; // (James08sep97) + slowStart = 0; + } + currentDir = 99;// this ensures that we don't put in turn frames for this walk or the next + } + // check each turn condition in turn + if (((lastDir != 99) && (currentDir != 99)) && (usingWalkingTurnFrames)) // only for george + { + lastDir = currentDir - lastDir;//1 and -7 going right -1 and 7 going left + if (((lastDir == -1) || (lastDir == 7)) || ((lastDir == -2) || (lastDir == 6))) + { + // turn at the end of the last walk + frame = lastCount - framesPerStep; + do + { + walkAnim[frame].frame += firstWalkingTurnLeftFrame; // was 104; //turning left + frame += 1; + } + while(frame < lastCount ); + } + if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6))) + { + // turn at the end of the current walk + frame = lastCount - framesPerStep; + do + { + walkAnim[frame].frame += firstWalkingTurnRightFrame; // was 200; // turning right + frame += 1; + } + while(frame < lastCount ); + } + } + // all turns checked + lastCount = stepCount; + } + } + } + p = p + 1; + lastDir = currentDir; + slowStart = 0; //can only be valid first time round + } + while (modularPath[p].dir < NO_DIRECTIONS); + + + //**************************************************************************** + // SOLID: THE SLOW OUT + AddSlowOutFrames(walkAnim); + + //**************************************************************************** + + module = framesPerChar + modularPath[p-1].dir; + walkAnim[stepCount].frame = module; + walkAnim[stepCount].step = 0; + walkAnim[stepCount].dir = modularPath[p-1].dir; + walkAnim[stepCount].x = moduleX; + walkAnim[stepCount].y = moduleY; + stepCount += 1; + + walkAnim[stepCount].frame = 512; + walkAnim[stepCount].step = 99; + stepCount += 1; + walkAnim[stepCount].frame = 512; + walkAnim[stepCount].step = 99; + stepCount += 1; + walkAnim[stepCount].frame = 512; + walkAnim[stepCount].step = 99; + + //------------------------------------------- + // write all the frames to "debug.txt" + + //Zdebug("\nTHE WALKDATA:"); + for (frame=0; frame<=stepCount; frame++) + { + //Zdebug("walkAnim[%d].frame=%d",frame,walkAnim[frame].frame); + } + //------------------------------------------- + + //**************************************************************************** + // SOLID + // NO END TURNS + //**************************************************************************** + +// Zdebug("RouteFinder RouteSize is %d", stepCount); +// now check the route + i = 0; + do + { + if (!Check(modularPath[i].x, modularPath[i].y, modularPath[i+1].x, modularPath[i+1].y)) + p=0; + #ifdef PLOT_PATHS + RouteLine(modularPath[i].x, modularPath[i].y, modularPath[i+1].x, modularPath[i+1].y, 227); + #endif + i += 1; + } + while(i<p-1); + if (p != 0) + { + targetDir = modularPath[p-1].dir; + } + if (p != 0) + { + if (CheckTarget(moduleX,moduleY) == 3)// new target on a line + { + p = 0; + //Zdebug("Solid walk target was on a line %d %d", moduleX, moduleY); + } + } + + return p; +} + + +/******************************************************************************* + ******************************************************************************* + * THE SCAN ROUTINES + ******************************************************************************* + *******************************************************************************/ + +int32 Scan(int32 level) +/******************************************************************************* + * Called successively from RouteFinder until no more changes take place in the + * grid array ie he best path has been found + * + * Scans through every point in the node array and checks if there is a route + * between each point and if this route gives a new route. + * + * This routine could probably halve its processing time if it doubled up on the + * checks after each route check + * + *******************************************************************************/ +{ + int32 i; + int32 k; + int32 x1; + int32 y1; + int32 x2; + int32 y2; + int32 distance; + int32 changed = 0; + // For all the nodes that have new values and a distance less than enddist + // ie dont check for new routes from a point we checked before or from a point + // that is already further away than the best route so far. + i = 0; + do + { + if ((node[i].dist < node[nnodes].dist) && (node[i].level == level)) + { + x1 = node[i].x; + y1 = node[i].y; + k=nnodes; + do + { + if (node[k].dist > node[i].dist) + { + x2 = node[k].x; + y2 = node[k].y; + + if (abs(x2-x1)>(4.5*abs(y2-y1))) + { + distance = (8*abs(x2-x1)+18*abs(y2-y1))/(54*8)+1; + } + else + { + distance = (6*abs(x2-x1)+36*abs(y2-y1))/(36*14)+1; + } + + if ((distance + node[i].dist < node[nnodes].dist) && (distance + node[i].dist < node[k].dist)) + { + if (NewCheck(0, x1,y1,x2,y2)) + { + node[k].level = level + 1; + node[k].dist = distance + node[i].dist; + node[k].prev = i; + changed = 1; + } + } + } + k-=1; + } + while(k > 0); + } + i=i+1; + } + while(i < nnodes); + return changed; +} + + +int32 NewCheck(int32 status, int32 x1 , int32 y1 , int32 x2 ,int32 y2) +/******************************************************************************* + * NewCheck routine checks if the route between two points can be achieved + * without crossing any of the bars in the Bars array. + * + * NewCheck differs from check in that that 4 route options are considered + * corresponding to actual walked routes. + * + * Note distance doesnt take account of shrinking ??? + * + * Note Bars array must be properly calculated ie min max dx dy co + *******************************************************************************/ +{ + int32 dx; + int32 dy; + int32 dlx; + int32 dly; + int32 dirX; + int32 dirY; + int32 step1; + int32 step2; + int32 step3; + int32 steps; + int32 options; + + steps = 0; + options = 0; + dx = x2 - x1; + dy = y2 - y1; + dirX = 1; + dirY = 1; + if (dx < 0) + { + dx = -dx; + dirX = -1; + } + + if (dy < 0) + { + dy = -dy; + dirY = -1; + } + + //make the route options + if ((diagonaly * dx) > (diagonalx * dy)) // dir = 1,2 or 2,3 or 5,6 or 6,7 + { + dly = dy; + dlx = (dy*diagonalx)/diagonaly; + dx = dx - dlx; + dlx = dlx * dirX; + dly = dly * dirY; + dx = dx * dirX; + dy = 0; + + //options are + //square, diagonal a code 1 route + step1 = Check(x1, y1, x1+dx, y1); + if (step1 != 0) + { + step2 = Check(x1+dx, y1, x2, y2); + if (step2 != 0) + { + steps = step1 + step2; // yes + options = options + 2; + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x1+dx, y1, 231); + RouteLine(x1+dx, y1, x2, y2, 231); + } + #endif + } + } + //diagonal, square a code 2 route + if ((steps == 0) || (status == 1)) + { + step1 = Check(x1, y1, x1+dlx,y1+dly); + if (step1 != 0) + { + step2 = Check(x1+dlx, y2, x2, y2); + if (step2 != 0) + { + steps = step1 + step2; // yes + options = options + 4; + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x1+dlx,y1+dly, 231); + RouteLine(x1+dlx, y2, x2, y2, 231); + } + #endif + } + } + } + //halfsquare, diagonal, halfsquare a code 0 route + if ((steps == 0) || (status == 1)) + { + step1 = Check(x1, y1, x1+dx/2, y1); + if (step1 != 0) + { + step2 = Check(x1+dx/2, y1, x1+dx/2+dlx, y2); + if (step2 != 0) + { + step3 = Check(x1+dx/2+dlx, y2, x2, y2); + if (step3 != 0) + { + steps = step1 + step2 + step3; // yes + options = options + 1; + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x1+dx/2, y1, 231); + RouteLine(x1+dx/2, y1, x1+dx/2+dlx, y2, 231); + RouteLine(x1+dx/2+dlx, y2, x2, y2, 231); + } + #endif + } + } + } + } + //halfdiagonal, square, halfdiagonal a code 3 route + if ((steps == 0) || (status == 1)) + { + step1 = Check(x1, y1, x1+dlx/2, y1+dly/2); + if (step1 != 0) + { + step2 = Check(x1+dlx/2, y1+dly/2, x1+dx+dlx/2, y1+dly/2); + if (step2 != 0) + { + step3 = Check(x1+dx+dlx/2, y1+dly/2, x2, y2); + if (step3 != 0) + { + steps = step1 + step2 + step3; // yes + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x1+dlx/2, y1+dly/2, 231); + RouteLine(x1+dlx/2, y1+dly/2, x1+dx+dlx/2, y1+dly/2, 231); + RouteLine(x1+dx+dlx/2, y1+dly/2, x2, y2, 231); + } + #endif + options = options + 8; + } + } + } + } + } + else // dir = 7,0 or 0,1 or 3,4 or 4,5 + { + dlx = dx; + dly = (dx*diagonaly)/diagonalx; + dy = dy - dly; + dlx = dlx * dirX; + dly = dly * dirY; + dy = dy * dirY; + dx = 0; + + //options are + //square, diagonal a code 1 route + step1 = Check(x1 ,y1 ,x1 ,y1+dy ); + if (step1 != 0) + { + step2 = Check(x1 ,y1+dy ,x2,y2); + if (step2 != 0) + { + steps = step1 + step2; // yes + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1 ,y1 ,x1 ,y1+dy, 231); + RouteLine(x1 ,y1+dy ,x2, y2, 231); + } + #endif + options = options + 2; + } + } + //diagonal, square a code 2 route + if ((steps == 0) || (status == 1)) + { + step1 = Check(x1, y1, x2, y1+dly); + if (step1 != 0) + { + step2 = Check(x2, y1+dly, x2, y2); + if (step2 != 0) + { + steps = step1 + step2; // yes + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x2, y1+dly, 231); + RouteLine(x2, y1+dly, x2, y2, 231); + } + #endif + options = options + 4; + } + } + } + //halfsquare, diagonal, halfsquare a code 0 route + if ((steps == 0) || (status == 1)) + { + step1 = Check(x1, y1, x1, y1+dy/2); + if (step1 != 0) + { + step2 = Check(x1, y1+dy/2, x2, y1+dy/2+dly); + if (step2 != 0) + { + step3 = Check(x2, y1+dy/2+dly, x2, y2); + if (step3 != 0) + { + steps = step1 + step2 + step3; // yes + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x1, y1+dy/2, 231); + RouteLine(x1, y1+dy/2, x2, y1+dy/2+dly, 231); + RouteLine(x2, y1+dy/2+dly, x2, y2, 231); + } + #endif + options = options + 1; + } + } + } + } + //halfdiagonal, square, halfdiagonal a code 3 route + if ((steps == 0) || (status == 1)) + { + step1 = Check(x1, y1, x1+dlx/2, y1+dly/2); + if (step1 != 0) + { + step2 = Check(x1+dlx/2, y1+dly/2, x1+dlx/2, y1+dy+dly/2); + if (step2 != 0) + { + step3 = Check(x1+dlx/2, y1+dy+dly/2, x2, y2); + if (step3 != 0) + { + steps = step1 + step2 + step3; // yes + options = options + 8; + #ifdef PLOT_PATHS + if (status == 1) + { + RouteLine(x1, y1, x1+dlx/2, y1+dly/2, 231); + RouteLine(x1+dlx/2, y1+dly/2, x1+dlx/2, y1+dy+dly/2, 231); + RouteLine(x1+dlx/2, y1+dy+dly/2, x2, y2, 231); + } + #endif + } + } + } + } + } + if (status == 0) + { + status = steps; + } + else + { + status = options; + } + return status; +} + + +/******************************************************************************* + ******************************************************************************* + * CHECK ROUTINES + ******************************************************************************* + *******************************************************************************/ + + +int32 Check(int32 x1 , int32 y1 , int32 x2 ,int32 y2) +{ +//call the fastest line check for the given line +//returns 1 if line didn't cross any bars + int32 steps; + + if ((x1 == x2) && (y1 == y2)) + { + steps = 1; + } + else if (x1 == x2) + { + steps = VertCheck(x1, y1, y2); + } + else if (y1 == y2) + { + steps = HorizCheck(x1, y1, x2); + } + else + { + steps = LineCheck(x1, y1, x2, y2); + } + return steps; + +} + + +int32 LineCheck(int32 x1 , int32 y1 , int32 x2 ,int32 y2) +{ + int32 dirx; + int32 diry; + int32 co; + int32 slope; + int32 i; + int32 xc; + int32 yc; + int32 xmin; + int32 ymin; + int32 xmax; + int32 ymax; + int32 linesCrossed = 1; + + + if (x1 > x2) + { + xmin = x2; + xmax = x1; + } + else + { + xmin = x1; + xmax = x2; + } + if (y1 > y2) + { + ymin = y2; + ymax = y1; + } + else + { + ymin = y1; + ymax = y2; + } + //line set to go one step in chosen direction + //so ignore if it hits anything + dirx = x2 - x1; + diry = y2 - y1; + co = (y1 *dirx)- (x1*diry); //new line equation + + i = 0; + do + { + // this is the inner inner loop + if ((xmax >= bars[i].xmin) && ( xmin <= bars[i].xmax)) //skip if not on module + { + if ((ymax >= bars[i].ymin) && ( ymin <= bars[i].ymax)) //skip if not on module + { + // okay its a valid line calculate an intersept + // wow but all this arithmatic we must have loads of time + slope = (bars[i].dx * diry) - (bars[i].dy *dirx);// slope it he slope between the two lines + if (slope != 0)//assuming parallel lines don't cross + { + //calculate x intercept and check its on both lines + xc = ((bars[i].co * dirx) - (co * bars[i].dx)) / slope; + + if ((xc >= xmin-1) && (xc <= xmax+1)) //skip if not on module + { + if ((xc >= bars[i].xmin-1) && (xc <= bars[i].xmax+1)) //skip if not on line + { + + yc = ((bars[i].co * diry) - (co * bars[i].dy)) / slope; + + if ((yc >= ymin-1) && (yc <= ymax+1)) //skip if not on module + { + if ((yc >= bars[i].ymin-1) && (yc <= bars[i].ymax+1)) //skip if not on line + { + linesCrossed = 0; + } + } + } + } + } + } + } + i = i + 1; + } + while((i < nbars) && linesCrossed); + + return linesCrossed; +} + + +int32 HorizCheck(int32 x1 , int32 y , int32 x2) +{ + int32 dy; + int32 i; + int32 xc; + int32 xmin; + int32 xmax; + int32 linesCrossed = 1; + + if (x1 > x2) + { + xmin = x2; + xmax = x1; + } + else + { + xmin = x1; + xmax = x2; + } + //line set to go one step in chosen direction + //so ignore if it hits anything + + i = 0; + do + { + // this is the inner inner loop + if ((xmax >= bars[i].xmin) && ( xmin <= bars[i].xmax)) //skip if not on module + { + if ((y >= bars[i].ymin) && ( y <= bars[i].ymax)) //skip if not on module + { + // okay its a valid line calculate an intersept + // wow but all this arithmatic we must have loads of time + if (bars[i].dy == 0) + { + linesCrossed = 0; + } + else + { + dy = y-bars[i].y1; + xc = bars[i].x1 + (bars[i].dx * dy)/bars[i].dy; + if ((xc >= xmin-1) && (xc <= xmax+1)) //skip if not on module + { + linesCrossed = 0; + } + } + } + } + i = i + 1; + } + while((i < nbars) && linesCrossed); + + return linesCrossed; +} + + +int32 VertCheck(int32 x, int32 y1, int32 y2) +{ + int32 dx; + int32 i; + int32 yc; + int32 ymin; + int32 ymax; + int32 linesCrossed = 1; + + if (y1 > y2) + { + ymin = y2; + ymax = y1; + } + else + { + ymin = y1; + ymax = y2; + } + //line set to go one step in chosen direction + //so ignore if it hits anything + i = 0; + do // this is the inner inner loop + { + if ((x >= bars[i].xmin) && ( x <= bars[i].xmax)) //overlapping + { + if ((ymax >= bars[i].ymin) && ( ymin <= bars[i].ymax)) //skip if not on module + { + // okay its a valid line calculate an intersept + // wow but all this arithmatic we must have loads of time + if (bars[i].dx == 0)//both lines vertical and overlap in x and y so they cross + { + linesCrossed = 0; + } + else + { + dx = x-bars[i].x1; + yc = bars[i].y1 + (bars[i].dy * dx)/bars[i].dx; + if ((yc >= ymin-1) && (yc <= ymax+1)) //the intersept overlaps + { + linesCrossed = 0; + } + } + } + } + i = i + 1; + } + while((i < nbars) && linesCrossed); + + return linesCrossed; +} + +int32 CheckTarget(int32 x , int32 y) +/******************************************************************************* + *******************************************************************************/ +{ + int32 dx; + int32 dy; + int32 i; + int32 xc; + int32 yc; + int32 xmin; + int32 xmax; + int32 ymin; + int32 ymax; + int32 onLine = 0; + + xmin = x - 1; + xmax = x + 1; + ymin = y - 1; + ymax = y + 1; + + // check if point +- 1 is on the line + //so ignore if it hits anything + + i = 0; + do + { + + // this is the inner inner loop + + if ((xmax >= bars[i].xmin) && ( xmin <= bars[i].xmax)) //overlapping line + { + if ((ymax >= bars[i].ymin) && ( ymin <= bars[i].ymax)) //overlapping line + { + + // okay this line overlaps the target calculate an y intersept for x + + if (bars[i].dx == 0)// vertical line so we know it overlaps y + { + yc = 0; + } + else + { + dx = x-bars[i].x1; + yc = bars[i].y1 + (bars[i].dy * dx)/bars[i].dx; + } + + if ((yc >= ymin) && (yc <= ymax)) //overlapping point for y + { + onLine = 3;// target on a line so drop out + //Zdebug("RouteFail due to target on a line %d %d",x,y); + } + else + { + if (bars[i].dy == 0)// vertical line so we know it overlaps y + { + xc = 0; + } + else + { + dy = y-bars[i].y1; + xc = bars[i].x1 + (bars[i].dx * dy)/bars[i].dy; + } + + if ((xc >= xmin) && (xc <= xmax)) //skip if not on module + { + onLine = 3;// target on a line so drop out + //Zdebug("RouteFail due to target on a line %d %d",x,y); + } + } + } + } + i = i + 1; + } + while((i < nbars) && (onLine == 0)); + + + return onLine; +} + +/******************************************************************************* + ******************************************************************************* + * THE SETUP ROUTINES + ******************************************************************************* + *******************************************************************************/ +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ + +void LoadWalkData(Object_walkdata *ob_walkdata) +{ + uint8 direction; + uint16 firstFrameOfDirection; + uint16 walkFrameNo; + uint32 frameCounter = 0; // starts at frame 0 of mega set (16sep96 JEL) + + + nWalkFrames = ob_walkdata->nWalkFrames; + usingStandingTurnFrames = ob_walkdata->usingStandingTurnFrames; + usingWalkingTurnFrames = ob_walkdata->usingWalkingTurnFrames; + usingSlowInFrames = ob_walkdata->usingSlowInFrames; + usingSlowOutFrames = ob_walkdata->usingSlowOutFrames; + numberOfSlowOutFrames = usingSlowOutFrames; // 0 = not using slow out frames; non-zero = using that many frames for each leading leg for each direction + + memcpy(&numberOfSlowInFrames[0],ob_walkdata->nSlowInFrames,NO_DIRECTIONS*sizeof(numberOfSlowInFrames[0])); + memcpy(&leadingLeg[0],ob_walkdata->leadingLeg,NO_DIRECTIONS*sizeof(leadingLeg[0])); + memcpy(&dx[0],ob_walkdata->dx,NO_DIRECTIONS*(nWalkFrames+1)*sizeof(dx[0])); + memcpy(&dy[0],ob_walkdata->dy,NO_DIRECTIONS*(nWalkFrames+1)*sizeof(dy[0])); + + //--------------------------------------------------------- + + for (direction=0; direction<NO_DIRECTIONS; direction++) + { + firstFrameOfDirection = direction * nWalkFrames; + + modX[direction]=0; + modY[direction]=0; + + for (walkFrameNo=firstFrameOfDirection; walkFrameNo < (firstFrameOfDirection + (nWalkFrames/2)); walkFrameNo++ ) + { + modX[direction] += dx[walkFrameNo]; // eg. modX[0] is the sum of the x-step sizes for the first half of the walk cycle for direction 0 + modY[direction] += dy[walkFrameNo]; + } + } + + diagonalx = modX[3]; + diagonaly = modY[3]; + + //---------------------------------------------------- + // interpret the walk data + //---------------------------------------------------- + + framesPerStep = nWalkFrames/2; + framesPerChar = nWalkFrames * NO_DIRECTIONS; + + // offset pointers added Oct 30 95 JPS + // mega id references removed 16sep96 by JEL + + //--------------------- + // WALK FRAMES + // start on frame 0 + frameCounter += framesPerChar; + + //--------------------- + // STAND FRAMES + firstStandFrame = frameCounter; // stand frames come after the walk frames + frameCounter += NO_DIRECTIONS; // one stand frame for each direction + + //--------------------- + // STANDING TURN FRAMES - OPTIONAL! + if (usingStandingTurnFrames) + { + firstStandingTurnLeftFrame = frameCounter; // standing turn-left frames come after the slow-out frames + frameCounter += NO_DIRECTIONS; // one for each direction + + firstStandingTurnRightFrame = frameCounter; // standing turn-left frames come after the standing turn-right frames + frameCounter += NO_DIRECTIONS; // one for each direction + } + else + { + firstStandingTurnLeftFrame = firstStandFrame; // refer instead to the normal stand frames + firstStandingTurnRightFrame = firstStandFrame; // -"- + } + //--------------------- + // WALKING TURN FRAMES - OPTIONAL! + if (usingWalkingTurnFrames) + { + firstWalkingTurnLeftFrame = frameCounter; // walking left-turn frames come after the stand frames + frameCounter += framesPerChar; + + firstWalkingTurnRightFrame = frameCounter; // walking right-turn frames come after the walking left-turn frames + frameCounter += framesPerChar; + } + else + { + firstWalkingTurnLeftFrame = 0; + firstWalkingTurnRightFrame = 0; + } + //--------------------- + // SLOW-IN FRAMES - OPTIONAL! + + if (usingSlowInFrames) // slow-in frames come after the walking right-turn frames + { + for (direction=0; direction<NO_DIRECTIONS; direction++) + { + firstSlowInFrame[direction] = frameCounter; // make note of frame number of first slow-in frame for each direction + frameCounter += numberOfSlowInFrames[direction]; // can be a different number of slow-in frames in each direction + } + } + //--------------------- + // SLOW-OUT FRAMES - OPTIONAL! + + if (usingSlowOutFrames) + { + firstSlowOutFrame = frameCounter; // slow-out frames come after the slow-in frames + } + //--------------------- +} + + +/******************************************************************************* + ******************************************************************************* + * THE ROUTE EXTRACTOR + ******************************************************************************* + *******************************************************************************/ + +void ExtractRoute() +/**************************************************************************** + * ExtractRoute gets route from the node data after a full scan, route is + * written with just the basic way points and direction options for heading + * to the next point. + ****************************************************************************/ +{ + int32 prev; + int32 prevx; + int32 prevy; + int32 last; + int32 point; + int32 p; + int32 dirx; + int32 diry; + int32 dir; + int32 dx; + int32 dy; + + + // extract the route from the node data + prev = nnodes; + last = prev; + point = O_ROUTE_SIZE - 1; + route[point].x = node[last].x; + route[point].y = node[last].y; + do + { + point = point - 1; + prev = node[last].prev; + prevx = node[prev].x; + prevy = node[prev].y; + route[point].x = prevx; + route[point].y = prevy; + last = prev; + } + while (prev > 0); + + // now shuffle route down in the buffer + routeLength = 0; + do + { + route[routeLength].x = route[point].x; + route[routeLength].y = route[point].y; + point = point + 1; + routeLength = routeLength + 1; + } + while (point < O_ROUTE_SIZE); + routeLength = routeLength - 1; + + // okay the route exists as a series point now put in some directions + p = 0; + do + { + #ifdef PLOT_PATHS + BresenhamLine(route[p+1].x-128,route[p+1].y-128, route[p].x-128,route[p].y-128, (uint8*)screen_ad, true_pixel_size_x, pixel_size_y, ROUTE_END_FLAG); + #endif + dx = route[p+1].x - route[p].x; + dy = route[p+1].y - route[p].y; + dirx = 1; + diry = 1; + if (dx < 0) + { + dx = -dx; + dirx = -1; + } + if (dy < 0) + { + dy = -dy; + diry = -1; + } + + if ((diagonaly * dx) > (diagonalx * dy)) // dir = 1,2 or 2,3 or 5,6 or 6,7 + { + dir = 4 - 2 * dirx; // 2 or 6 + route[p].dirS = dir; + dir = dir + diry * dirx; // 1,3,5 or 7 + route[p].dirD = dir; + } + else // dir = 7,0 or 0,1 or 3,4 or 4,5 + { + dir = 2 + 2 * diry; // 0 or 4 + route[p].dirS = dir; + dir = 4 - 2 * dirx; // 2 or 6 + dir = dir + diry * dirx; // 1,3,5 or 7 + route[p].dirD = dir; + } + p = p + 1; + } + while (p < (routeLength)); + // set the last dir to continue previous route unless specified + if (targetDir == 8) // ANY direction + { + route[p].dirS = route[p-1].dirS; + route[p].dirD = route[p-1].dirD; + } + else + { + route[p].dirS = targetDir; + route[p].dirD = targetDir; + } + return; +} + +//******************************************************************************* + +void RouteLine(int32 x1,int32 y1,int32 x2,int32 y2 ,int32 colour) +{ + if (x1); + if (x2); + if (y1); + if (y2); + if (colour); +// BresenhamLine(x1-128, y1-128, x2-128, y2-128, (uint8*)screen_ad, true_pixel_size_x, pixel_size_y, colour); + return; +} + +//******************************************************************************* + +void SetUpWalkGrid(Object_mega *ob_mega, int32 x, int32 y, int32 dir) +{ + int32 i; + + + LoadWalkGrid(); // get walk grid file + extra grid into 'bars' & 'node' arrays + + + // copy the mega structure into the local variables for use in all subroutines + + startX = ob_mega->feet_x; + startY = ob_mega->feet_y; + startDir = ob_mega->current_dir; + targetX = x; + targetY = y; + targetDir = dir; + + scaleA = ob_mega->scale_a; + scaleB = ob_mega->scale_b; + + + // mega's current position goes into first node + node[0].x = startX; + node[0].y = startY; + node[0].level = 1; + node[0].prev = 0; + node[0].dist = 0; + + // reset other nodes + for (i=1; i<nnodes; i++) + { + node[i].level = 0; + node[i].prev = 0; + node[i].dist = 9999; + } + + // target position goes into final node + node[nnodes].x = targetX; + node[nnodes].y = targetY; + node[nnodes].level = 0; + node[nnodes].prev = 0; + node[nnodes].dist = 9999; +} + +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ +void PlotWalkGrid(void) +{ + int32 j; + + + LoadWalkGrid(); // get walk grid file + extra grid into 'bars' & 'node' arrays + + //------------------------------- + // lines + + for (j=0; j<nbars; j++) + { + DrawLine(bars[j].x1,bars[j].y1, bars[j].x2,bars[j].y2, 254); + } + //------------------------------- + // nodes + + for (j=1; j<nnodes; j++) // leave node 0 for start node + { + PlotCross(node[j].x,node[j].y, 184); + } + //------------------------------- +} +//------------------------------------------------------------------------------------------ +void PlotCross(int16 x, int16 y, uint8 colour) +{ + DrawLine(x-1, y-1, x+1, y+1, colour); + DrawLine(x+1, y-1, x-1, y+1, colour); +} +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ + +void LoadWalkGrid(void) +{ +// _standardHeader header; + _walkGridHeader floorHeader; + uint32 j; + uint8 *fPolygrid; + int entry; + uint32 theseBars; + uint32 theseNodes; + + + nbars = 0; // reset counts + nnodes = 1; // leave node 0 for start-node + + //------------------------------- + // STATIC GRIDS (added/removed by object logics) + + for (entry=0; entry < MAX_WALKGRIDS; entry++) // go through walkgrid list + { + if (walkGridList[entry]) + { + fPolygrid = res_man.Res_open(walkGridList[entry]); // open walk grid file + + // memmove( (uint8*)&header, fPolygrid, sizeof(_standardHeader) ); + fPolygrid += sizeof(_standardHeader); + + memmove( (uint8*)&floorHeader, fPolygrid, sizeof(_walkGridHeader) ); + fPolygrid += sizeof(_walkGridHeader); + + //------------------------------- + // how many bars & nodes are we getting from this walkgrid file + + theseBars = floorHeader.numBars; + theseNodes = floorHeader.numNodes; + + //------------------------------- + // check that we're not going to exceed the max allowed in the complete walkgrid arrays + + #ifdef _DEBUG + if ((nbars+theseBars) >= O_GRID_SIZE) + Con_fatal_error("Adding walkgrid(%d): %d+%d bars exceeds max %d (%s line %u)", walkGridList[entry], nbars, theseBars, O_GRID_SIZE, __FILE__, __LINE__); + + if ((nnodes+theseNodes) >= O_GRID_SIZE) + Con_fatal_error("Adding walkgrid(%d): %d+%d nodes exceeds max %d (%s line %u)", walkGridList[entry], nnodes, theseBars, O_GRID_SIZE, __FILE__, __LINE__); + #endif + + //------------------------------- + // lines + + memmove( (uint8*)&bars[nbars], fPolygrid, theseBars*sizeof(_barData) ); + fPolygrid += theseBars*sizeof(_barData);//move pointer to start of node data + + //------------------------------- + // nodes + + for (j=0; j<theseNodes; j++) // leave node 0 for start node + { + memmove( (uint8*)&node[nnodes+j].x, fPolygrid, 2*sizeof(int16) ); + fPolygrid += 2*sizeof(int16); + } + + //------------------------------- + + res_man.Res_close(walkGridList[entry]); // close walk grid file + + nbars += theseBars; // increment counts of total bars & nodes in whole walkgrid + nnodes += theseNodes; + } + } + + //------------------------------- + // EXTRA GRIDS (moveable grids added by megas) + + // Note that these will be checked against allowed max at the time of creating them + + //------------------------------- + // extra lines + + memmove((uint8 *) &bars[nbars], (uint8 *) &extraBars[0], nExtraBars*sizeof(_barData)); + nbars += nExtraBars; + + //------------------------------- + // extra nodes + + memmove((uint8 *) &node[nnodes], (uint8 *) &extraNode[0], nExtraNodes*sizeof(_nodeData)); + nnodes += nExtraNodes; + + //------------------------------- +} + +//------------------------------------------------------------------------------------------ +void ClearWalkGridList(void) +{ + int entry; + + for (entry=0; entry < MAX_WALKGRIDS; entry++) + walkGridList[entry] = 0; +} +//------------------------------------------------------------------------------------------ +// called from FN_add_walkgrid +void AddWalkGrid(int32 gridResource) +{ + int entry; + + // first, scan list to see if this grid is already included + entry=0; + while ((entry < MAX_WALKGRIDS) && (walkGridList[entry] != gridResource)) + entry++; + + if (entry == MAX_WALKGRIDS) // if this new resource isn't already in the list, then add it, (otherwise finish) + { + // scan the list for a free slot + entry=0; + while ((entry < MAX_WALKGRIDS) && (walkGridList[entry])) + entry++; + + if (entry < MAX_WALKGRIDS) // if we found a free slot + walkGridList[entry] = gridResource; + else + Con_fatal_error("ERROR: walkGridList[] full in %s line %d",__FILE__,__LINE__); + } +} +//-------------------------------------------------------------------------------------- +// called from FN_remove_walkgrid +void RemoveWalkGrid(int32 gridResource) +{ + int entry; + + // first, scan list to see if this grid is actually there + entry=0; + while ((entry < MAX_WALKGRIDS) && (walkGridList[entry] != gridResource)) + entry++; + + if (entry < MAX_WALKGRIDS) // if we've found it in the list, reset entry to zero (otherwise just ignore the request) + walkGridList[entry] = 0; +} +//-------------------------------------------------------------------------------------- + diff --git a/sword2/router.h b/sword2/router.h new file mode 100644 index 0000000000..447bdc9d33 --- /dev/null +++ b/sword2/router.h @@ -0,0 +1,58 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _ROUTER_H +#define _ROUTER_H + +//#include "src\driver96.h" +#include "memory.h" +#include "object.h" + + +typedef struct _walkData +{ + uint16 frame; + int16 x; + int16 y; + uint8 step; + uint8 dir; +} _walkData; + + + + +int32 RouteFinder(Object_mega *ob_mega, Object_walkdata *ob_walkdata, int32 x, int32 y, int32 dir); + +void EarlySlowOut(Object_mega *ob_mega, Object_walkdata *ob_walkdata); + +void AllocateRouteMem(void); +_walkData* LockRouteMem(void); +void FloatRouteMem(void); +void FreeRouteMem(void); +void FreeAllRouteMem(void); +void PlotWalkGrid(void); +void AddWalkGrid(int32 gridResource); +void RemoveWalkGrid(int32 gridResource); +void ClearWalkGridList(void); +uint8 CheckForCollision(void); + +//-------------------------------------------------------------------------------------- + + +#endif diff --git a/sword2/save_rest.cpp b/sword2/save_rest.cpp new file mode 100644 index 0000000000..10a374a7a3 --- /dev/null +++ b/sword2/save_rest.cpp @@ -0,0 +1,556 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +// SAVE_REST.CPP save, restore & restart functions +// +// James 05feb97 +// +// "Jesus Saves", but could he Restore or Restart? He can now... +// +//------------------------------------------------------------------------------------ + +//#include <direct.h> directx? +#include <stdio.h> +#include <stdlib.h> + +#include "driver/driver96.h" +#include "console.h" +#include "defs.h" +#include "function.h" // for engine_logic, engine_graph, etc +#include "interpreter.h" // for IR_CONT, etc +#include "layers.h" +#include "logic.h" +#include "memory.h" +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "router.h" +#include "save_rest.h" +#include "scroll.h" // for Set_scrolling() +#include "sound.h" +#include "walker.h" + +//------------------------------------------------------------------------------------ +#define MAX_FILENAME_LEN 128 // max length of a savegame filename, including full path + +//------------------------------------------------------------------------------------ +// local function prototypes + +void GetPlayerStructures(void); // James27feb97 +void PutPlayerStructures(void); // James27feb97 + +uint32 SaveData(uint16 slotNo, uint8 *buffer, uint32 bufferSize); +uint32 RestoreData(uint16 slotNo, uint8 *buffer, uint32 bufferSize); + +uint32 CalcChecksum(uint8 *buffer, uint32 size); // James04aug97 + +//------------------------------------------------------------------------------------ + +typedef struct // savegame file header (James06feb97) +{ + uint32 checksum; // sum of all bytes in file, excluding this uint32 + char description[SAVE_DESCRIPTION_LEN]; // player's description of savegame + uint32 varLength; // length of global variables resource + uint32 screenId; // resource id of screen file + uint32 runListId; // resource id of run list + uint32 feet_x; // copy of this_screen.feet_x + uint32 feet_y; // copy of this_screen.feet_y + uint32 music_id; // copy of 'looping_music_id' + _object_hub player_hub; // copy of player object's object_hub structure + Object_logic logic; // copy of player character logic structure + Object_graphic graphic; // copy of player character graphic structure + Object_mega mega; // copy of player character mega structure +} +_savegameHeader; + +// savegame consists of header & global variables resource + +_savegameHeader header; // global because easier to copy to/from player object structures + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +// SAVE GAME +//------------------------------------------------------------------------------------ + +uint32 SaveGame(uint16 slotNo, uint8 *desc) // (James05feb97) +{ + mem *saveBufferMem; + uint32 bufferSize; + uint32 errorCode; + + //------------------------------------------------------ + // allocate the savegame buffer + + bufferSize = FindBufferSize(); + saveBufferMem = Twalloc( bufferSize, MEM_locked, UID_savegame_buffer ); + + FillSaveBuffer(saveBufferMem, bufferSize, desc); + + //------------------------------------------------------ + // save it (platform-specific) + + errorCode = SaveData( slotNo, saveBufferMem->ad, bufferSize ); // save the buffer + + //------------------------------------------------------ + // free the buffer + + Free_mem( saveBufferMem ); + + //------------------------------------------------------ + + return(errorCode); +} + +//------------------------------------------------------------------------------------ +// calculate size of required savegame buffer + +uint32 FindBufferSize( void ) +{ + return (sizeof(header) + res_man.Res_fetch_len(1)); // size of savegame header + size of global variables +} + +//------------------------------------------------------------------------------------ + +void FillSaveBuffer(mem *buffer, uint32 size, uint8 *desc) +{ + uint8 *varsRes; + + //------------------------------------------------------ + // set up the header + + // 'checksum' gets filled in last of all + sprintf(header.description, "%s", (char*)desc); // player's description of savegame + header.varLength = res_man.Res_fetch_len(1); // length of global variables resource + header.screenId = this_screen.background_layer_id; // resource id of current screen file + header.runListId = LLogic.Return_run_list(); // resource id of current run-list + header.feet_x = this_screen.feet_x; // those scroll position control things + header.feet_y = this_screen.feet_y; // + header.music_id = looping_music_id; // id of currently looping music (or zero) + + // object hub + memcpy (&header.player_hub, res_man.Res_open(CUR_PLAYER_ID) + sizeof(_standardHeader), sizeof(_object_hub)); + res_man.Res_close(CUR_PLAYER_ID); + + // logic, graphic & mega structures + GetPlayerStructures(); // copy the 4 essential player object structures into the header + + //------------------------------------------------------ + // copy the header to the buffer + + memcpy( buffer->ad, &header, sizeof(header) ); // copy the header to the savegame buffer + + //------------------------------------------------------ + // copy the global variables to the buffer + + varsRes = res_man.Res_open(1); // open variables resource + memcpy( buffer->ad + sizeof(header), varsRes, header.varLength ); // copy that to the buffer, following the header + res_man.Res_close(1); // close variables resource + + //------------------------------------------------------ + // set the checksum & copy that to the buffer (James05aug97) + + header.checksum = CalcChecksum((buffer->ad)+sizeof(header.checksum), size-sizeof(header.checksum)); + memcpy( buffer->ad, &header.checksum, sizeof(header.checksum) ); // copy the header to the savegame buffer + + //------------------------------------------------------ +} + +//------------------------------------------------------------------------------------ + +uint32 SaveData(uint16 slotNo, uint8 *buffer, uint32 bufferSize) +{ + char saveFileName[MAX_FILENAME_LEN]; + FILE *fp; + uint32 itemsWritten; + + +//create saves directory just in case not there + _mkdir("saves"); + + + sprintf(saveFileName, "saves\\savegame.%.3d", slotNo); // construct filename + + fp = fopen(saveFileName, "wb"); // attempt to open file for writing + + if (fp==NULL) + { + return(SR_ERR_FILEOPEN); // error: couldn't open file + } + else + { +// itemsWritten = fwrite(sourceAddress, size, count, fp); + itemsWritten = fwrite(buffer, 1, bufferSize, fp); // write the buffer + fclose(fp); // close savegame file + + if (itemsWritten == bufferSize) // if we successfully wrote it all + return(SR_OK); // buffer saved ok + else + return(SR_ERR_WRITEFAIL); // write failed for some reason (could be hard drive full) + } +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +// RESTORE GAME +//------------------------------------------------------------------------------------ + +uint32 RestoreGame(uint16 slotNo) // (James05feb97) +{ + mem *saveBufferMem; + uint32 bufferSize; + uint32 errorCode; + + //------------------------------------------------------ + // allocate the savegame buffer + + bufferSize = FindBufferSize(); + saveBufferMem = Twalloc( bufferSize, MEM_locked, UID_savegame_buffer ); + + //------------------------------------------------------ + // read the savegame file into our buffer + + errorCode = RestoreData( slotNo, saveBufferMem->ad, bufferSize ); // load savegame into buffer + + //------------------------------------------------------ + // if it was read in successfully, then restore the game from the buffer & free the buffer + + if (errorCode == SR_OK) + { + errorCode = RestoreFromBuffer(saveBufferMem, bufferSize); + + // Note that the buffer has been freed inside RestoreFromBuffer, + // in order to clear it from memory before loading in the new screen & runlist + } + else + Free_mem( saveBufferMem ); // because RestoreFromBuffer would have freed it + + //------------------------------------------------------ + + return(errorCode); // game restored ok +} + +//------------------------------------------------------------------------------------ + +uint32 RestoreData(uint16 slotNo, uint8 *buffer, uint32 bufferSize) +{ + char saveFileName[MAX_FILENAME_LEN]; + FILE *fp; + uint32 itemsRead; + + + sprintf(saveFileName, "saves\\savegame.%.3d", slotNo); // construct filename + + fp = fopen(saveFileName, "rb"); // attempt to open file for reading + + if (fp==NULL) + { + return(SR_ERR_FILEOPEN); // error: couldn't open file + } + else + { +// itemsRead = fread(destAddress, size, count, fp); + itemsRead = fread(buffer, 1, bufferSize, fp); // read savegame into the buffer + + if (itemsRead == bufferSize) // if we successfully read it all + { + fclose(fp); // close savegame file + return(SR_OK); // file read ok + } + else // didn't read the expected amount of data for some reason + { + if (ferror(fp)) // if it was a genuine read error, before reaching the end of the file + { + fclose(fp); // close savegame file + return(SR_ERR_READFAIL); // error: read failed + } + else // we reached the end of the file before we filled the savegame buffer (ie. incompatible savegame file!) + { + fclose(fp); // close savegame file + return(SR_ERR_INCOMPATIBLE); // error: incompatible save-data - can't use! + } + } + } +} + +//------------------------------------------------------------------------------------ +uint32 RestoreFromBuffer(mem *buffer, uint32 size) +{ + uint8 *varsRes; + int32 pars[2]; + + memcpy( &header, buffer->ad, sizeof(header) ); // get a copy of the header from the savegame buffer + + //------------------------------------------------------ + // Calc checksum & check that aginst the value stored in the header (James05aug97) + + if (header.checksum != CalcChecksum((buffer->ad)+sizeof(header.checksum), size-sizeof(header.checksum))) + { + Free_mem( buffer ); + return(SR_ERR_INCOMPATIBLE); // error: incompatible save-data - can't use! + } + //------------------------------------------------------ + // check savegame against length of current global variables resource + // This would most probably be trapped by the checksum test anyway, but it doesn't do any harm to check this as well + + // Note that during development, earlier savegames will often be shorter than the current expected length + + if (header.varLength != res_man.Res_fetch_len(1)) // if header contradicts actual current size of global variables + { + Free_mem( buffer ); + return(SR_ERR_INCOMPATIBLE); // error: incompatible save-data - can't use! + } + //---------------------------------- + // clean out system + res_man.Kill_all_res(0); // trash all resources from memory except player object & global variables + LLogic.Reset_kill_list(); // clean out the system kill list (no more objects to kill) + + //---------------------------------- + // get player character data from savegame buffer + + // object hub is just after the standard header + memcpy (res_man.Res_open(CUR_PLAYER_ID) + sizeof(_standardHeader), &header.player_hub, sizeof(_object_hub)); + res_man.Res_close(CUR_PLAYER_ID); + PutPlayerStructures(); // fill in the 4 essential player object structures from the header + + //---------------------------------- + // get variables resource from the savegame buffer + + varsRes = res_man.Res_open(1); // open variables resource + memcpy( varsRes, buffer->ad + sizeof(header), header.varLength );// copy that to the buffer, following the header + res_man.Res_close(1); // close variables resource + + Free_mem( buffer ); // free it now, rather than in RestoreGame, to unblock memory before new screen & runlist loaded + pars[0] = header.screenId; + pars[1] = 1; + FN_init_background(pars); + this_screen.new_palette=99; // (JEL08oct97) so palette not restored immediately after control panel - we want to fade up instead! + + this_screen.feet_x = header.feet_x; // these need setting after the defaults get set in FN_init_background + this_screen.feet_y = header.feet_y; // remember that these can change through the game, so need saving & restoring too + LLogic.Express_change_session(header.runListId); // start the new run list + + //---------------------------------------------------------------------------- + // (James01aug97) + // Force in the new scroll position, so unsightly scroll-catch-up does not occur + // when screen first draws after returning from restore panel + + this_screen.player_feet_x = header.mega.feet_x; // set 'this_screen's record of player position + this_screen.player_feet_y = header.mega.feet_y; // - ready for Set_scrolling() + + if (this_screen.scroll_flag) // if this screen is wide + Set_scrolling(); // recompute the scroll offsets now, + + //---------------------------------------------------------------------------- + // Any music required will be started after we've returned from Restore_control() + // - see System_menu() in mouse.cpp! + looping_music_id = header.music_id; + //------------------------------------------------------ + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"*************************************"); + Zdebug(0,"RESTORED GAME \"%s\"", header.description); + Zdebug(0,"*************************************"); + + // Also write this to system debug file + Zdebug("*************************************"); + Zdebug("RESTORED GAME \"%s\"", header.description); + Zdebug("*************************************"); + #endif + //-------------------------------------- + + + return(SR_OK); // game restored ok + +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +// GetSaveDescription - PC version... +//------------------------------------------------------------------------------------ + +uint32 GetSaveDescription(uint16 slotNo, uint8 *description) // (James05feb97) +{ + char saveFileName[MAX_FILENAME_LEN]; + _savegameHeader header; + FILE *fp; + + sprintf(saveFileName, "saves\\savegame.%.3d", slotNo); // construct filename + + fp = fopen(saveFileName, "rb"); // attempt to open file for reading + + if (fp==NULL) + { + return(SR_ERR_FILEOPEN); // error: couldn't open file + } + else + { +// fread(destAddress, size, count, fp); + fread(&header, sizeof(header), 1, fp); // read header + fclose(fp); + sprintf((char*)description, header.description); + return(SR_OK); + } +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void GetPlayerStructures(void) // James27feb97 +{ + // request the player object structures which need saving + + uint32 null_pc=7; // script no. 7 - 'george_savedata_request' calls FN_pass_player_savedata + char *raw_script_ad; + _standardHeader *head; + + + head = (_standardHeader*) res_man.Res_open(CUR_PLAYER_ID); + + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("incorrect CUR_PLAYER_ID=%d (%s line %u)",CUR_PLAYER_ID,__FILE__,__LINE__); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + RunScript( raw_script_ad, raw_script_ad, &null_pc ); + res_man.Res_close(CUR_PLAYER_ID); +} +//------------------------------------------------------------------------------------ +void PutPlayerStructures(void) // James27feb97 (updated by James on 29july97) +{ + // fill out the player object structures from the savegame structures + // also run the appropriate scripts to set up george's anim tables & walkdata, and nico's anim tables + + uint32 null_pc=8; // script no. 8 - 'george_savedata_return' calls FN_get_player_savedata + char *raw_script_ad; + _standardHeader *head; + + + head = (_standardHeader*) res_man.Res_open(CUR_PLAYER_ID); + + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("incorrect CUR_PLAYER_ID=%d (%s line %u)",CUR_PLAYER_ID,__FILE__,__LINE__); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + + null_pc=8; // script no. 8 - 'george_savedata_return' calls FN_get_player_savedata + RunScript( raw_script_ad, raw_script_ad, &null_pc ); + + null_pc=14; // script no. 14 - 'set_up_nico_anim_tables' + RunScript( raw_script_ad, raw_script_ad, &null_pc ); + + switch (header.mega.megaset_res) // which megaset was the player at the time of saving? + { + case 36: // GeoMega: + null_pc=9; // script no.9 - 'player_is_george' + break; + + case 2003: // GeoMegaB: + null_pc=13; // script no.13 - 'player_is_georgeB' + break; + + case 1366: // NicMegaA: + null_pc=11; // script no.11 - 'player_is_nicoA' + break; + + case 1437: // NicMegaB: + null_pc=12; // script no.12 - 'player_is_nicoB' + break; + + case 1575: // NicMegaC: + null_pc=10; // script no.10 - 'player_is_nicoC' + break; + } + RunScript( raw_script_ad, raw_script_ad, &null_pc ); + + res_man.Res_close(CUR_PLAYER_ID); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +int32 FN_pass_player_savedata(int32 *params) // James27feb97 +{ + // copies the 4 essential player structures into the savegame header + // - run script 7 of player object to request this + + // remember, we cannot simply read a compact any longer but instead must request it from the object itself + + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + + // copy from player object to savegame header + memcpy( &header.logic, (uint8*)params[0], sizeof(Object_logic) ); + memcpy( &header.graphic, (uint8*)params[1], sizeof(Object_graphic) ); + memcpy( &header.mega, (uint8*)params[2], sizeof(Object_mega) ); + + return(IR_CONT); //makes no odds +} +//------------------------------------------------------------------------------------ +int32 FN_get_player_savedata(int32 *params) // James27feb97 +{ + // reverse of FN_pass_player_savedata + // - run script 8 of player object + + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + + Object_logic *ob_logic = (Object_logic*) params[0]; + Object_graphic *ob_graphic = (Object_graphic*) params[1]; + Object_mega *ob_mega = (Object_mega*) params[2]; + + int32 pars[3]; + + + // copy from savegame header to player object + memcpy( (uint8*)ob_logic, &header.logic, sizeof(Object_logic) ); + memcpy( (uint8*)ob_graphic, &header.graphic, sizeof(Object_graphic) ); + memcpy( (uint8*)ob_mega, &header.mega, sizeof(Object_mega) ); + + + // any walk-data must be cleared - the player will be set to stand if he was walking when saved + + if (ob_mega->currently_walking) // if the player was walking when game was saved + { + ob_mega->currently_walking = 0; // clear the flag + ob_mega->colliding = 0; // reset this just in case + + pars[0] = (int32)ob_graphic; // pointer to object's graphic structure + pars[1] = (int32)ob_mega; // pointer to object's mega structure + pars[2] = ob_mega->current_dir; // target direction + FN_stand(pars); // set player to stand + + ob_logic->looping = 0; // reset looping flag (which would have been '1' during FN_walk) + } + + + return(IR_CONT); //makes no odds +} +//------------------------------------------------------------------------------------ +uint32 CalcChecksum(uint8 *buffer, uint32 size) // (James05aug97) +{ + uint32 total=0; + uint32 pos; + + for (pos=0; pos<size; pos++) + total += buffer[pos]; + + return(total); +} diff --git a/sword2/save_rest.h b/sword2/save_rest.h new file mode 100644 index 0000000000..b59c52fe45 --- /dev/null +++ b/sword2/save_rest.h @@ -0,0 +1,47 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef SAVE_REST_H +#define SAVE_REST_H + + +//#include "src\driver96.h" +#include "memory.h" + +#define SAVE_DESCRIPTION_LEN 64 + +uint32 SaveGame(uint16 slotNo, uint8 *description); +uint32 RestoreGame(uint16 slotNo); +uint32 GetSaveDescription(uint16 slotNo, uint8 *description); +void FillSaveBuffer(mem *buffer, uint32 size, uint8 *desc); +uint32 RestoreFromBuffer(mem *buffer, uint32 size); +uint32 FindBufferSize( void ); + + +// Save & Restore error codes + +// ERROR CODE VALUE MEANING REASON +// ========== ===== ======= ====== +#define SR_OK 0x00000000 // ok No worries +#define SR_ERR_FILEOPEN 0x00000001 // can't open file Could create file for saving, or couldn't find file for loading +#define SR_ERR_INCOMPATIBLE 0x00000002 // (RestoreGame only) incompatible savegame data Savegame file is obsolete. (Won't happen after development stops) +#define SR_ERR_READFAIL 0x00000003 // (RestoreGame only) failed on reading savegame file Something screwed up during the fread() +#define SR_ERR_WRITEFAIL 0x00000004 // (SaveGame only) failed on writing savegame file Something screwed up during the fwrite() - could be hard-drive full..? + +#endif diff --git a/sword2/scroll.cpp b/sword2/scroll.cpp new file mode 100644 index 0000000000..65148e00f3 --- /dev/null +++ b/sword2/scroll.cpp @@ -0,0 +1,154 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +//#include "src\driver96.h" +#include "build_display.h" +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "interpreter.h" +#include "layers.h" +#include "memory.h" +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "scroll.h" + +//------------------------------------------------------------------------------------ +#define MAX_SCROLL_DISTANCE 8 // max no of pixel allowed to scroll per cycle + +//------------------------------------------------------------------------------------ +uint8 scroll_fraction=16; // used to be a define, but now it's flexible (see new functions below) +//------------------------------------------------------------------------------------ +void Set_scrolling(void) //S2.1(2Mar94jel) refurnished Tony25Sept96 :-) +{ + // feet_x = 128+320 // normally we aim to get George's feet at (320,250) from top left of screen window + // feet_y = 128+250 + + // set scroll offsets according to the player's coords + + int16 offset_x; + int16 offset_y; + int16 dx, dy; + uint16 scroll_distance_x; // how much we want to scroll + uint16 scroll_distance_y; + + + if (SCROLL_X || SCROLL_Y) // if the scroll offsets are being forced in script (05feb97 JAMES) + { + // ensure not too far right + if (SCROLL_X < this_screen.max_scroll_offset_x) + this_screen.scroll_offset_x = SCROLL_X; + else + this_screen.scroll_offset_x = this_screen.max_scroll_offset_x; + + // ensure not too far down + if (SCROLL_Y < this_screen.max_scroll_offset_y) + this_screen.scroll_offset_y = SCROLL_Y; + else + this_screen.scroll_offset_y = this_screen.max_scroll_offset_y; + } + else + { + offset_x = this_screen.player_feet_x-this_screen.feet_x; // George's offset from the centre - the desired position for him + offset_y = this_screen.player_feet_y-this_screen.feet_y; + + + // prevent scrolling too far left/right/up/down + offset_x = offset_x < 0 ? 0 : ( (uint32)offset_x > this_screen.max_scroll_offset_x ? this_screen.max_scroll_offset_x : offset_x ); + offset_y = offset_y < 0 ? 0 : ( (uint32)offset_y > this_screen.max_scroll_offset_y ? this_screen.max_scroll_offset_y : offset_y ); + + if (this_screen.scroll_flag==2) // first time on this screen - need absolute scroll immediately! + { + //Zdebug(42,"init scroll"); + this_screen.scroll_offset_x = offset_x; + this_screen.scroll_offset_y = offset_y; + this_screen.scroll_flag=1; + } + else // catch up with required scroll offsets - speed depending on distance to catch up (dx and dy) & 'SCROLL_FRACTION' used + { // but limit to certain number of pixels per cycle (MAX_SCROLL_DISTANCE) + + dx = this_screen.scroll_offset_x - offset_x; + dy = this_screen.scroll_offset_y - offset_y; + + if (dx < 0) // current scroll_offset_x is less than the required value + { + scroll_distance_x = (1+(-dx)/scroll_fraction); // => inc by (fraction of the differnce) NB. dx is -ve, so we subtract dx/SCROLL_FRACTION + this_screen.scroll_offset_x += scroll_distance_x < MAX_SCROLL_DISTANCE ? scroll_distance_x : MAX_SCROLL_DISTANCE; + } + else if (dx > 0) // current scroll_offset_x is greater than the required value + { + scroll_distance_x = (1+dx/scroll_fraction); // => dec by (fraction of the differnce) + this_screen.scroll_offset_x -= scroll_distance_x < MAX_SCROLL_DISTANCE ? scroll_distance_x : MAX_SCROLL_DISTANCE; + } // NB. I'm adding 1 to the result of dx/SCROLL_FRACTION, because it would otherwise + // not scroll at all when dx < SCROLL_FRACTION + if (dy < 0) + { + scroll_distance_y = (1+(-dy)/scroll_fraction); + this_screen.scroll_offset_y += scroll_distance_y < MAX_SCROLL_DISTANCE ? scroll_distance_y : MAX_SCROLL_DISTANCE; + } + else if (dy > 0) + { + scroll_distance_y = (1+dy/scroll_fraction); + this_screen.scroll_offset_y -= scroll_distance_y < MAX_SCROLL_DISTANCE ? scroll_distance_y : MAX_SCROLL_DISTANCE; + } + } + } +} +//------------------------------------------------------------------------------------ +int32 FN_set_scroll_coordinate(int32 *params) //Tony25Sept96 +{ + // set the special scroll offset variables + // call when starting screens and to change the camera within screens + // call AFTER FN_init_background() to override the defaults + // called feet_x and feet_y to retain intelectual compatibility with Sword1 ! + // feet_x & feet_y refer to the physical screen coords where the system will try to maintain George's feet + + // param 0 feet_x value + // param 1 feet_y value + + + this_screen.feet_x = params[0]; + this_screen.feet_y = params[1]; + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +int32 FN_set_scroll_speed_normal(int32 *params) // James08aug97 +{ + scroll_fraction=16; + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_set_scroll_speed_slow(int32 *params) // James08aug97 +{ + scroll_fraction=32; + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ + diff --git a/sword2/scroll.h b/sword2/scroll.h new file mode 100644 index 0000000000..649ffb71f1 --- /dev/null +++ b/sword2/scroll.h @@ -0,0 +1,29 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//the usual suspects + +#ifndef _SCROLL +#define _SCROLL + +//#include "src\driver96.h" + +void Set_scrolling(void); + +#endif diff --git a/sword2/sound.cpp b/sword2/sound.cpp new file mode 100644 index 0000000000..e355d8f412 --- /dev/null +++ b/sword2/sound.cpp @@ -0,0 +1,504 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//-------------------------------------------------------------------------------------- +// BROKEN SWORD 2 +// +// SOUND.CPP Contains the sound engine, fx & music functions +// Some very 'sound' code in here ;) +// +// (16Dec96 JEL) +// +//-------------------------------------------------------------------------------------- + +#include <stdio.h> + +//#include "src\driver96.h" +#include "console.h" +#include "defs.h" // for RESULT +#include "interpreter.h" +#include "protocol.h" // for FetchObjectName() for debugging FN_play_fx +#include "resman.h" +#include "sound.h" + +//-------------------------------------------------------------------------------------- +typedef struct +{ + uint32 resource; // resource id of sample + uint32 fetchId; // Id of resource in PSX CD queue. :) + uint16 delay; // cycles to wait before playing (or 'random chance' if FX_RANDOM) + uint8 volume; // 0..16 + int8 pan; // -16..16 + uint8 type; // FX_SPOT, FX_RANDOM or FX_LOOP +} _fxq_entry; + +#define FXQ_LENGTH 32 // max number of fx in queue at once [DO NOT EXCEED 255] + +_fxq_entry fxq[FXQ_LENGTH]; + +//-------------------------------------------------------------------------------------- + +uint32 looping_music_id=0; // used to store id of tunes that loop, for save & restore +char musicDirectory[120]; + +//-------------------------------------------------------------------------------------- +// local function prototypes + +void Trigger_fx (uint8 j); + +//-------------------------------------------------------------------------------------- +// initialise the fxq by clearing all the entries + +void Init_fx_queue(void) +{ + uint8 j; + + + for (j=0; j < FXQ_LENGTH; j++) // scan the queue + { + fxq[j].resource = 0; // 0 resource means 'empty' slot + fxq[j].fetchId = 0; // Not being fetched. + } +} + +//-------------------------------------------------------------------------------------- +// process the fxq once every game cycle + +void Process_fx_queue(void) +{ + uint8 j; // assuming FXQ_LENGTH is 255 or less + + + for (j=0; j < FXQ_LENGTH; j++) // scan the queue + { + if (fxq[j].resource) // if this entry isn't empty + { + if (fxq[j].type == FX_RANDOM) // if it's type FX_RANDOM + { + if (rand()%(fxq[j].delay)==0) // 1 in 'delay' chance of this fx occurring + { + Trigger_fx(j); // play it + } + + } + else if(fxq[j].type == FX_SPOT) + { + if (fxq[j].delay) // if delay is above 0 + fxq[j].delay--; // decrement delay countdown + else // if zero delay remaining + { + Trigger_fx(j); // play it + fxq[j].type = FX_SPOT2; + } + } + else if (fxq[j].type == FX_SPOT2) + { + if (IsFxOpen(j+1)) + fxq[j].resource = 0; // Once the Fx has finished remove it from the queue. + } + } + } +} + +//-------------------------------------------------------------------------------------- +void Trigger_fx(uint8 j) // called from Process_fx_queue only +{ + uint8 *data; + int32 id; + uint32 rv; + + id = (uint32)j+1; // because 0 is not a valid id + + if (fxq[j].type == FX_SPOT) + { + data = res_man.Res_open(fxq[j].resource); // load in the sample + data += sizeof(_standardHeader); + rv = PlayFx( id, data, fxq[j].volume, fxq[j].pan, RDSE_FXSPOT ); // wav data gets copied to sound memory + res_man.Res_close(fxq[j].resource); // release the sample +// fxq[j].resource = 0; // clear spot fx from queue + } + else // random & looped fx are already loaded into sound memory by FN_play_fx() + { // - to be referenced by 'j', so pass NULL data + + if (fxq[j].type == FX_RANDOM) + rv = PlayFx( id, NULL, fxq[j].volume, fxq[j].pan, RDSE_FXSPOT ); // not looped + else // FX_LOOP + rv = PlayFx( id, NULL, fxq[j].volume, fxq[j].pan, RDSE_FXLOOP ); // looped + } + + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: PlayFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif +} + +//-------------------------------------------------------------------------------------- +int32 FN_play_fx(int32 *params) // called from script only +{ + // params: 0 sample resource id + // 1 type (FX_SPOT, FX_RANDOM, FX_LOOP) + // 2 delay (0..65535) + // 3 volume (0..16) + // 4 pan (-16..16) + + // example script: FN_play_fx (FXWATER, FX_LOOP, 0, 10, 15); + // fx_water = result; // fx_water is just a local script flag + // . + // . + // . + // FN_stop_fx (fx_water); + + uint8 j=0; + uint8 *data; + uint32 id; + uint32 rv; + + //---------------------------------- + #ifdef _DEBUG + + _standardHeader *header; + char type[10]; + + + if (wantSfxDebug) + { + switch (params[1]) // 'type' + { + case FX_SPOT: + strcpy(type,"SPOT"); + break; + + case FX_LOOP: + strcpy(type,"LOOPED"); + break; + + case FX_RANDOM: + strcpy(type,"RANDOM"); + break; + + default: + strcpy(type,"INVALID"); + } + + Zdebug("SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", FetchObjectName(params[0]), params[3], params[4], params[2], type); + } + + #endif _DEBUG + //---------------------------------- + + while ((j < FXQ_LENGTH) && (fxq[j].resource != 0)) + j++; + + if (j==FXQ_LENGTH) + { + return (IR_CONT); +// Con_fatal_error("ERROR: Sound queue overflow in FN_play_fx() (%s line %u)",__FILE__,__LINE__); + } + else + { + fxq[j].resource = params[0]; // wav resource id + fxq[j].type = params[1]; // FX_SPOT, FX_LOOP or FX_RANDOM + + if (fxq[j].type == FX_RANDOM) // FX_RANDOM: + fxq[j].delay = params[2] * 12 + 1; // 'delay' param is the intended average no. seconds between playing this effect (+1 to avoid divide-by-zero in Process_fx_queue) + else // FX_SPOT or FX_LOOP: + fxq[j].delay = params[2]; // 'delay' is no. frames to wait before playing + + fxq[j].volume = params[3]; // 0..16 + fxq[j].pan = params[4]; // -16..16 + + + if (fxq[j].type == FX_SPOT) // spot fx + { + #ifdef _DEBUG + data = res_man.Res_open(fxq[j].resource); // "pre-load" the sample; this gets it into memory + header = (_standardHeader*)data; + if (header->fileType != WAV_FILE) + Con_fatal_error("FN_play_fx given invalid resource (%s line %u)",__FILE__,__LINE__); + #else + res_man.Res_open(fxq[j].resource); // "pre-load" the sample; this gets it into memory + #endif + res_man.Res_close(fxq[j].resource); // but then releases it to "age" out if the space is needed + } + else // random & looped fx + { + id = (uint32)j+1; // because 0 is not a valid id + + data = res_man.Res_open(fxq[j].resource); // load in the sample + + #ifdef _DEBUG + header = (_standardHeader*)data; + if (header->fileType != WAV_FILE) + Con_fatal_error("FN_play_fx given invalid resource (%s line %u)",__FILE__,__LINE__); + #endif + + data += sizeof(_standardHeader); + rv = OpenFx(id,data); // copy it to sound memory, using position in queue as 'id' + + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: OpenFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif + + res_man.Res_close(fxq[j].resource); // release the sample + } + } + + + //--------------------------------------------- + // (James07uag97) + if (fxq[j].type == FX_LOOP) // looped fx + Trigger_fx(j); // play now, rather than in Process_fx_queue where it was getting played again & again! + //--------------------------------------------- + + + RESULT = j; // in case we want to call FN_stop_fx() later, to kill this fx (mainly for FX_LOOP & FX_RANDOM) + + return(IR_CONT); // continue script +} + +//-------------------------------------------------------------------------------------- +int32 FN_sound_fetch(int32 *params) +{ + return (IR_CONT); +} +//-------------------------------------------------------------------------------------- +// to alter the volume and pan of a currently playing fx +int32 FN_set_fx_vol_and_pan(int32 *params) +{ +// params 0 id of fx (ie. the id returned in 'result' from FN_play_fx +// 1 new volume (0..16) +// 2 new pan (-16..16) + +// SetFxVolumePan(int32 id, uint8 vol, uint8 pan); + SetFxVolumePan(1+params[0], params[1], params[2]); // driver fx_id is 1+<pos in queue> +// Zdebug("%d",params[2]); + + return (IR_CONT); +} +//-------------------------------------------------------------------------------------- +// to alter the volume of a currently playing fx +int32 FN_set_fx_vol(int32 *params) +{ +// params 0 id of fx (ie. the id returned in 'result' from FN_play_fx +// 1 new volume (0..16) + +// SetFxIdVolume(int32 id, uint8 vol); + SetFxIdVolume(1+params[0], params[1]); + + return (IR_CONT); +} +//-------------------------------------------------------------------------------------- +int32 FN_stop_fx(int32 *params) // called from script only +{ + // params: 0 position in queue + + // This will stop looped & random fx instantly, and remove the fx from the queue. + // So although it doesn't stop spot fx, it will remove them from the queue if they haven't yet played + + uint8 j = (uint8) params[0]; + uint32 id; + uint32 rv; + + if ((fxq[j].type == FX_RANDOM) || (fxq[j].type == FX_LOOP)) + { + id = (uint32)j+1; // because 0 is not a valid id + rv = CloseFx(id); // stop fx & remove sample from sound memory + + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: CloseFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif + } + + fxq[j].resource = 0; // remove from queue + + + return(IR_CONT); // continue script +} + +//-------------------------------------------------------------------------------------- +int32 FN_stop_all_fx(int32 *params) // called from script only +{ + // Stops all looped & random fx and clears the entire queue + // NO PARAMS + + Clear_fx_queue(); + + return(IR_CONT); // continue script +} +//-------------------------------------------------------------------------------------- +// Stops all looped & random fx and clears the entire queue + +void Clear_fx_queue(void) +{ + ClearAllFx(); // stop all fx & remove the samples from sound memory + Init_fx_queue(); // clean out the queue +} + +//-------------------------------------------------------------------------------------- + +//============================================================================= +// int32 StreamMusic(uint8 *filename, int32 loopFlag) +// +// Streams music from the file defined by filename. The loopFlag should +// be set to RDSE_FXLOOP if the music is to loop back to the start. +// Otherwise, it should be RDSE_FXSPOT. +// The return value must be checked for any problems. +// +// -------------------------------------------------------------------------- +// +// int32 PauseMusic(void) +// +// Stops the music dead in it's tracks. +// +// -------------------------------------------------------------------------- +// +// int32 UnpauseMusic(void) +// +// Re-starts the music from where it was stopped. +// +//============================================================================= +int32 FN_prepare_music(int32 *params) +{ + return (IR_CONT); +} + +//-------------------------------------------------------------------------------------- +// Start a tune playing, to play once or to loop until stopped or next one played +int32 FN_play_music(int32 *params) // updated by James on 10apr97 +{ + // params 0 tune id + // 1 loop flag (0 or 1) + + char filename[128]; + uint32 loopFlag; + uint32 rv; // drivers return value + + +// Zdebug("FN_play_music(%d)", params[0]); + + if (params[1]==FX_LOOP) // if it is to loop + { + loopFlag = RDSE_FXLOOP; + looping_music_id = params[0]; // keep a note of the id, for restarting after an interruption to gameplay + } + else // just play once + { + loopFlag = RDSE_FXSPOT; + looping_music_id = 0; // don't need to restart this tune after control panel or restore + } + + + // add the appropriate file extension & play it + + #ifdef _WEBDEMO // (James 01oct97) + sprintf(filename,"MUSIC.CLU"); + #else + sprintf(filename,"%sCLUSTERS\\MUSIC.CLU", res_man.GetCdPath()); + #endif // _WEBDEMO + + rv = StreamCompMusic(filename, params[0], loopFlag); + + #ifdef _DEBUG + if (rv) + Zdebug("ERROR: StreamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv); + #endif + +// Zdebug("FN_play_music(%d) returning", params[0]); + + return(IR_CONT); // continue script +} + +//-------------------------------------------------------------------------------------- +int32 FN_stop_music(int32 *params) // called from script only +{ + // params: none + + + looping_music_id=0; // clear the 'looping' flag + + StopMusic(); + + if (params); + + return(IR_CONT); // continue script +} +//-------------------------------------------------------------------------------------- +extern void UpdateCompSampleStreaming(void); // used in Kill_music() +//-------------------------------------------------------------------------------------- +void Kill_music(void) // James22aug97 +{ + uint8 count; + + looping_music_id=0; // clear the 'looping' flag + StopMusic(); + + // THIS BIT CAUSES THE MUSIC TO STOP INSTANTLY! + for(count=0; count<16; count++) + UpdateCompSampleStreaming(); +} +//-------------------------------------------------------------------------------------- +int32 FN_check_music_playing(int32 *params) // James (30july97) +{ + + // params: none + // sets result to no. of seconds of current tune remaining + // or 0 if no music playing + + RESULT = MusicTimeRemaining(); // in seconds, rounded up to the nearest second + + return(IR_CONT); // continue script +} +//-------------------------------------------------------------------------------------- +void PauseAllSound(void) // James25july97 +{ + uint32 rv; // for drivers return value + + rv = PauseMusic(); + if (rv != RD_OK) + Zdebug("ERROR: PauseMusic() returned %.8x in PauseAllSound()", rv); + + rv = PauseSpeech(); + if (rv != RD_OK) + Zdebug("ERROR: PauseSpeech() returned %.8x in PauseAllSound()", rv); + + rv = PauseFx(); + if (rv != RD_OK) + Zdebug("ERROR: PauseFx() returned %.8x in PauseAllSound()", rv); +} +//-------------------------------------------------------------------------------------- +void UnpauseAllSound(void) // James25july97 +{ + uint32 rv; // for drivers return value + + rv = UnpauseMusic(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseMusic() returned %.8x in UnpauseAllSound()", rv); + + rv = UnpauseSpeech(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseSpeech() returned %.8x in UnpauseAllSound()", rv); + + rv = UnpauseFx(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseFx() returned %.8x in UnpauseAllSound()", rv); +} +//-------------------------------------------------------------------------------------- + diff --git a/sword2/sound.h b/sword2/sound.h new file mode 100644 index 0000000000..bca28ad66f --- /dev/null +++ b/sword2/sound.h @@ -0,0 +1,55 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +/***************************************************************************** + * SOUND.H Sound engine + * + * SOUND.CPP Contains the sound engine, fx & music functions + * Some very 'sound' code in here ;) + * + * (16Dec96 JEL) + * + ****************************************************************************/ + +#ifndef SOUND_H +#define SOUND_H + +#include "scummsys.h" + +// fx types +#define FX_SPOT 0 +#define FX_LOOP 1 +#define FX_RANDOM 2 +#define FX_SPOT2 3 + +void Init_fx_queue(void); // to be called during system initialisation +void Process_fx_queue(void); // to be called from the main loop, once per cycle +void Clear_fx_queue(void); // stops all fx & clears the queue - eg. when leaving a location +void PauseAllSound(void); // James25july97 +void UnpauseAllSound(void); // James25july97 + +void Kill_music(void); // James22aug97 + + +int32 FN_play_music(int32 *params); // for save_Rest.cpp +int32 FN_stop_music(int32 *params); + +extern uint32 looping_music_id; // used to store id of tunes that loop, for save & restore + +#endif diff --git a/sword2/speech.cpp b/sword2/speech.cpp new file mode 100644 index 0000000000..b7101c2c0b --- /dev/null +++ b/sword2/speech.cpp @@ -0,0 +1,1994 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +#include <io.h> // for access() +#include <stdio.h> + +//#include "src\driver96.h" +#include "anims.h" +#include "console.h" +#include "controls.h" // for 'subtitles' & 'speechSelected' +#include "debug.h" +#include "defs.h" +#include "events.h" +#include "function.h" +#include "interpreter.h" +#include "layers.h" // for 'this_screen' +#include "logic.h" +#include "maketext.h" +#include "memory.h" +#include "mouse.h" +#include "object.h" +#include "protocol.h" +#include "resman.h" +#include "sound.h" +#include "speech.h" +#include "walker.h" +//------------------------------------------------------------------------------------ + +#define INS_talk 1 +#define INS_anim 2 +#define INS_reverse_anim 3 +#define INS_walk 4 +#define INS_turn 5 +#define INS_face 6 +#define INS_trace 7 +#define INS_no_sprite 8 +#define INS_sort 9 +#define INS_foreground 10 +#define INS_background 11 +#define INS_table_anim 12 +#define INS_reverse_table_anim 13 +#define INS_walk_to_anim 14 +#define INS_set_frame 15 +#define INS_stand_after_anim 16 + +#define INS_quit 42 + +//------------------------------------------------------------------------------------ + +uint32 speech_time=0; //when not playing a wav we calculate the speech time based upon length of ascii + +uint32 speech_text_bloc_no=0; +uint32 anim_id=0; +uint32 speech_anim_type; //0 lip synced and repeating - 1 normal once through +uint32 left_click_delay=0; // click-delay for LEFT mouse button +uint32 right_click_delay=0; // click-delay for RIGHT mouse button + +uint32 default_response_id=0; // ref number for default response when luggage icon is used on a person + // & it doesn't match any of the icons which would have been in the chooser +int16 officialTextNumber=0; // "TEXT" - current official text line number - will match the wav filenames + +int32 speechScriptWaiting = 0; // usually 0; if non-zero then it's the id of whoever we're waiting for in a speech script + // see FN_they_do, FN_they_do_we_wait & FN_we_wait + +int16 text_x, text_y; // calculated by LocateTalker() for use in speech-panning & text-sprite positioning + +_subject_unit subject_list[MAX_SUBJECT_LIST]; + + +//------------------------------------------------------------------------------------ +// local function prototypes + +int32 FN_i_speak(int32 *params); +void LocateTalker(int32 *params); // (James 01july97) +void Form_text(int32 *params); //Tony18Oct96 +BOOL Is_anim_boxed(uint32 res); //Tony20Oct96 +uint8 WantSpeechForLine(uint32 wavId); // James (29july97) + +#ifdef _DEBUG +void GetCorrectCdForSpeech(int32 wavId); // for testing speech & text +#endif + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +int32 FN_add_subject(int32 *params) // James12nov96 / Tony18Nov96 +{ +// param[0] id +// param[1] daves reference number + + if (IN_SUBJECT==0) // if this is the start of the new subject list (James 07may97) + default_response_id=0; // set the default repsonse id to zero in case we're never passed one + // - this just means we'd get the response for the 1st icon in the chooser + // which is better than crashing + + if (params[0] == -1) // this isn't an icon at all, it's telling us the id of the default response + { + default_response_id = params[1]; // and here it is - this is the ref number we will return if + // a luggage icon is clicked on someone when it wouldn't have + // been in the chooser list (see FN_choose below) + } + else + { + subject_list[IN_SUBJECT].res=params[0]; + subject_list[IN_SUBJECT].ref=params[1]; + +// Zdebug("FN_add_subject res %d, uid %d", params[0], params[1]); + + IN_SUBJECT+=1; + } + + + return(IR_CONT); // continue script +} +//------------------------------------------------------------------------------------ +int choosing=0; //could alternately use logic->looping of course + +int32 FN_choose(int32 *params) //Tony19Nov96 +{ +//no params + +//the human is switched off so there will be no normal mouse engine + + + _mouseEvent *me; + uint32 j,hit; + uint8 *icon; + uint32 pos=0; + +// Zdebug("into choose"); + + + AUTO_SELECTED=0; // see below (James23may97) + + + //------------------------------------------- + // new thing to intercept objects held at time of clicking on a person + // (James 06may97) + + if (OBJECT_HELD) // if we are using a luggage icon on the person + { + // scan the subject list to see if this icon would have been available at this time + // if it is there, return the relevant 'ref' number (as if it had been selected from within the conversation) + // if not, just return a special code to get the default text line(s) for unsupported objects + // Note that we won't display the subject icons in this case! + + while (pos < IN_SUBJECT) // scan the subject list for a match with our 'object_held' + { + if (subject_list[pos].res == OBJECT_HELD) // if we've found a match + { + OBJECT_HELD=0; // clear it so it doesn't keep happening! + IN_SUBJECT=0; // clear the subject list + return(IR_CONT+(subject_list[pos].ref<<3)); // return special subject chosen code (same as in normal chooser routine below) + } + pos++; // next position + } + + OBJECT_HELD=0; // clear it so it doesn't keep happening! + IN_SUBJECT=0; // clear the subject list + return(IR_CONT+(default_response_id<<3)); // so that the speech script uses the default text for objects that are not accounted for + } + //------------------------------------------- + // new thing for skipping chooser with "nothing else to say" text + // (James 23may97) + + // If this is the 1st time the chooser is coming up in this conversation + // AND there's only 1 subject + // AND it's the EXIT icon + if ((CHOOSER_COUNT_FLAG==0) && (IN_SUBJECT==1) && (subject_list[0].res == EXIT_ICON)) + { + AUTO_SELECTED=1; // for speech script + + IN_SUBJECT=0; // clear the subject list + return(IR_CONT+(subject_list[0].ref<<3)); // return special subject chosen code (same as in normal chooser routine below) + } + //------------------------------------------- + + + if (!choosing) //new choose session + { + + +// build menus from subject_list + + if (!IN_SUBJECT) + Con_fatal_error("FN_choose with no subjects :-O"); + +//init top menu from master list + for (j=0;j<15;j++) //all icons are highlighted / full colour + { + if (j<IN_SUBJECT) + { +// Zdebug(" ICON res %d for %d", subject_list[j].res, j); + icon = res_man.Res_open( subject_list[j].res ) + sizeof(_standardHeader) + RDMENU_ICONWIDE*RDMENU_ICONDEEP; + SetMenuIcon(RDMENU_BOTTOM, j, icon); + res_man.Res_close( subject_list[j].res ); + } + else + { SetMenuIcon(RDMENU_BOTTOM, j, NULL); //no icon here + //Zdebug(" NULL for %d", j); + } + } + +// start menus appearing + ShowMenu(RDMENU_BOTTOM); + + +// lets have the mouse pointer back + Set_mouse(NORMAL_MOUSE_ID); + + choosing=1; + + return(IR_REPEAT); //again next cycle + } + + else //menu is there - we're just waiting for a click + { +// Zdebug("choosing"); + me = MouseEvent(); //get mouse event + +// we only care about left clicks +// we ignore mouse releases + + if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) + { +// check for click on a menu +// if so then end the choose, highlight only the chosen, blank the mouse and return the ref code *8 + + if ((mousey>399)&&(mousex>=24)&&(mousex<640-24)) + { + hit=(mousex-24)/40; //which are we over? + + if (hit<IN_SUBJECT) //clicked on something - what button? + { + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"----------------------"); + Zdebug(0,"Icons available:"); + #endif + //-------------------------------------- + + for (j=0;j<IN_SUBJECT;j++) //change icons + { + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"%s", FetchObjectName(subject_list[j].res)); + #endif + //-------------------------------------- + + if (j!=hit) //change all others to grey + { + icon = res_man.Res_open( subject_list[j].res ) + sizeof(_standardHeader); //now grey + SetMenuIcon(RDMENU_BOTTOM, j, icon); + res_man.Res_close( subject_list[j].res ); + } + } + + //-------------------------------------- + // Write to walkthrough file (zebug0.txt) + #ifdef _DEBUG + Zdebug(0,"Selected: %s", FetchObjectName(subject_list[hit].res)); + Zdebug(0,"----------------------"); + #endif + //-------------------------------------- + + choosing=0; //this is our looping flag + + IN_SUBJECT=0; + + Set_mouse(NULL); //blank mouse again + +// Zdebug("hit %d - ref %d ref*8 %d", hit, subject_list[hit].ref, subject_list[hit].ref*8); + + RESULT=subject_list[hit].res; //for non-speech scripts that manually call the chooser + + return(IR_CONT+(subject_list[hit].ref<<3)); //return special subject chosen code + } + } + } +// Zdebug("end choose"); + return(IR_REPEAT); //again next cycle + } +} +//------------------------------------------------------------------------------------ +int32 FN_start_conversation(int32 *params) //Tony27Nov96 +{ +//Start conversation + +//FN_no_human(); // an FN_no_human +//FN_change_speech_text (PLAYER, GEORGE_WIDTH, GEORGE_PEN ); + +//params: 0 <empty> + +// Zdebug("FN_start_conversation %d", ID); + + //-------------------------------------------------------------- + // reset 'chooser_count_flag' at the start of each conversation: + + // Note that FN_start_conversation might accidently be called + // every time the script loops back for another chooser + // but we only want to reset the chooser count flag the first time this function is called + // ie. when talk flag is zero + if (TALK_FLAG==0) + CHOOSER_COUNT_FLAG=0; // see FN_chooser & speech scripts + //-------------------------------------------------------------- + + FN_no_human(params); + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +int32 FN_end_conversation(int32 *params) //Tony27Nov96 +{ +//end conversation +//talk_flag=0; +//FN_end_chooser(); +//FN_add_human(); +//FN_change_speech_text (PLAYER, VOICE_OVER_WIDTH, VOICE_OVER_PEN ); +//FN_idle(); +//params: 0 <empty> + + +// Zdebug("FN_end_conversation"); + + HideMenu(RDMENU_BOTTOM); + + if (mousey>399) + { mouse_mode=MOUSE_holding; //will wait for cursor to move off the bottom menu + Zdebug(" holding"); + } + + TALK_FLAG=0; //in-case DC forgets + + +// restart george's base script +// LLogic.Total_restart(); + + if (params); + + return(IR_CONT); //drop out without saving pc and go around again +} +//------------------------------------------------------------------------------------ +int32 FN_they_do(int32 *params) //S2.1(18Jan95tw) Tony3Dec96 +{ +//doesn't send the command until target is waiting - once sent we carry on + +//params 0 target +// 1 command +// 2 ins1 +// 3 ins2 +// 4 ins3 +// 5 ins4 +// 6 ins5 + + uint32 null_pc=5; //4th script - get-speech-state + char *raw_script_ad; + _standardHeader *head; + int32 target=params[0]; + + +//request status of target + head = (_standardHeader*) res_man.Res_open(target); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("FN_they_do %d not an object", target); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + res_man.Res_close(target); + +//result is 1 for waiting, 0 for busy + + if ((RESULT==1)&&(!INS_COMMAND)) //its waiting and no other command is queueing + { + speechScriptWaiting = 0; // reset debug flag now that we're no longer waiting - see debug.cpp + + SPEECH_ID = params[0]; + INS_COMMAND = params[1]; + INS1 = params[2]; + INS2 = params[3]; + INS3 = params[4]; + INS4 = params[5]; + INS5 = params[6]; + + return(IR_CONT); //script cont + } + + speechScriptWaiting = target; // debug flag to indicate who we're waiting for - see debug.cpp + return(IR_REPEAT); // target is busy so come back again next cycle +} +//------------------------------------------------------------------------------------ +int32 FN_they_do_we_wait(int32 *params) //Tony3Dec96 +{ +//give target a command and wait for it to register as finished + +//params 0 pointer to ob_logic +// 1 target +// 2 command +// 3 ins1 +// 4 ins2 +// 5 ins3 +// 6 ins4 +// 7 ins5 + +//'looping' flag is used as a sent command yes/no + + Object_logic *ob_logic; + + uint32 null_pc=5; //4th script - get-speech-state + char *raw_script_ad; + _standardHeader *head; + int32 target=params[1]; + + +// Zdebug("FN_they_do_we_wait id %d, command %d", params[1], params[2]); + + +// ok, see if the target is busy - we must request this info from the target object + head = (_standardHeader*) res_man.Res_open(target); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("FN_they_do_we_wait %d not an object", target); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + res_man.Res_close(target); + + + + + + ob_logic = (Object_logic *)params[0]; + + if ((!INS_COMMAND)&&(RESULT==1)&&(ob_logic->looping==0)) //first time so set up targets command if target is waiting + { + +// Zdebug("FNtdww sending command to %d", target); + + SPEECH_ID = params[1]; + INS_COMMAND = params[2]; + INS1 = params[3]; + INS2 = params[4]; + INS3 = params[5]; + INS4 = params[6]; + INS5 = params[7]; + + ob_logic->looping=1; + + speechScriptWaiting = target; // debug flag to indicate who we're waiting for - see debug.cpp + return(IR_REPEAT); // finish this cycle - but come back again to check for it being finished + } + else if (ob_logic->looping==0) //did not send the command + { + speechScriptWaiting = target; // debug flag to indicate who we're waiting for - see debug.cpp + return(IR_REPEAT); // come back next go and try again to send the instruction + } + + +//ok, the command has been sent - has the target actually done it yet? + +//result is 1 for waiting, 0 for busy + + if (RESULT==1) // its waiting now so we can be finished with all this + { +// Zdebug("FNtdww finished"); + ob_logic->looping=0; // not looping anymore + + speechScriptWaiting = 0; // reset debug flag now that we're no longer waiting - see debug.cpp + return(IR_CONT); // script cont + } + +// Zdebug("FNtdww just waiting"); + + speechScriptWaiting = target; // debug flag to indicate who we're waiting for - see debug.cpp + return(IR_REPEAT); // see ya next cycle +} +//------------------------------------------------------------------------------------ +int32 FN_we_wait(int32 *params) //Tony3Dec96 +{ +//loop until the target is free + +//params 0 target + + uint32 null_pc=5; //4th script - get-speech-state + char *raw_script_ad; + _standardHeader *head; + int32 target=params[0]; + +//request status of target + head = (_standardHeader*) res_man.Res_open(target); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("FN_we_wait %d not an object", target); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + res_man.Res_close(target); + +//result is 1 for waiting, 0 for busy + + if (RESULT==1) + { + speechScriptWaiting = 0; // reset debug flag now that we're no longer waiting - see debug.cpp + return(IR_CONT); // script cont + } + + speechScriptWaiting = target; // debug flag to indicate who we're waiting for - see debug.cpp + return(IR_REPEAT); // target is busy so come back again next cycle +} +//------------------------------------------------------------------------------------ +int32 FN_timed_wait(int32 *params) //Tony12Dec96 +{ +//loop until the target is free but only while the timer is high +//useful when clicking on a target to talk to them - if they never reply then this'll fall out avoiding a lock up + +//params 0 ob_logic +// 1 target +// 2 number of cycles before give up + + uint32 null_pc=5; //4th script - get-speech-state + char *raw_script_ad; + Object_logic *ob_logic; + _standardHeader *head; + int32 target=params[1]; + + + ob_logic = (Object_logic *)params[0]; + + + if (!ob_logic->looping) + ob_logic->looping=params[2]; //first time in + + +//request status of target + head = (_standardHeader*) res_man.Res_open(target); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("FN_timed_wait %d not an object", target); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + res_man.Res_close(target); + +//result is 1 for waiting, 0 for busy + + ob_logic->looping--; + + if (RESULT==1) //its waiting + { + ob_logic->looping=0; //reset because counter is likely to be still high + RESULT=0; //means ok + + speechScriptWaiting = 0; // reset debug flag now that we're no longer waiting - see debug.cpp + return(IR_CONT); // script cont + } + + + if (!ob_logic->looping) //time up - caller must check RESULT + { + + RESULT=1; //not ok + +// kill the event + + Kill_all_ids_events(target); //clear the event that hasn't been picked up - in theory, none of this should ever happen + + Zdebug("EVENT timed out"); + + speechScriptWaiting = 0; // reset debug flag now that we're no longer waiting - see debug.cpp + return(IR_CONT); //script cont + } + + speechScriptWaiting = target; // debug flag to indicate who we're waiting for - see debug.cpp + return(IR_REPEAT); // target is busy so come back again next cycle +} +//------------------------------------------------------------------------------------ +int32 FN_speech_process(int32 *params) //Tony5Dec96 +{ +//recieve and sequence the commands sent from the conversation script +//we have to do this in a slightly tweeky manner as we can no longer have generic scripts +//this function comes in with all the structures that will be required + +//param 0 pointer to ob_graphic +//param 1 pointer to ob_speech +//param 2 pointer to ob_logic +//param 3 pointer to ob_mega +//param 4 pointer to ob_walkdata + +//note - we could save a var and ditch wait_state and check 'command' for non zero means busy + + Object_speech *ob_speech; + + int32 pars[9]; + int32 ret; + + + ob_speech = (Object_speech*) params[1]; + + +// Zdebug(" SP"); + + while(1) + { + if (ob_speech->command) //we are currently running a command + { + switch(ob_speech->command) + { + //---------------------------------------------- + case INS_talk: + + pars[0]=params[0]; //ob_graphic + pars[1]=params[1]; //ob_speech + pars[2]=params[2]; //ob_logic + pars[3]=params[3]; //ob_mega + + pars[4]=ob_speech->ins1; //param 4 encoded text number + pars[5]=ob_speech->ins2; //param 5 wav res id + pars[6]=ob_speech->ins3; //param 6 anim res id + pars[7]=ob_speech->ins4; //param 7 anim table res id + pars[8]=ob_speech->ins5; //param 8 animation mode 0 lip synced, 1 just straight animation + + // Zdebug("speech-process talk"); + + ret = FN_i_speak(pars); //run the function - (it thinks its been called from script - bloody fool) + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command +// Zdebug("speech-process talk finished"); + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_turn: + + pars[0]=params[2]; // ob_logic + pars[1]=params[0]; // ob_graphic + pars[2]=params[3]; // ob_mega + pars[3]=params[4]; // ob_walkdata + pars[4]=ob_speech->ins1; // direction to turn to + + ret = FN_turn(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_face: + + pars[0]=params[2]; // ob_logic + pars[1]=params[0]; // ob_graphic + pars[2]=params[3]; // ob_mega + pars[3]=params[4]; // ob_walkdata + pars[4]=ob_speech->ins1; // target + + ret = FN_face_mega(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_anim: + + pars[0]=params[2]; //ob_logic + pars[1]=params[0]; //ob_graphic + pars[2]=ob_speech->ins1; //anim res + + ret= FN_anim(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_reverse_anim: + + pars[0]=params[2]; //ob_logic + pars[1]=params[0]; //ob_graphic + pars[2]=ob_speech->ins1; //anim res + + ret= FN_reverse_anim(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_table_anim: + + pars[0]=params[2]; //ob_logic + pars[1]=params[0]; //ob_graphic + pars[2]=params[3]; //ob_mega + pars[3]=ob_speech->ins1; //pointer to anim table + + ret= FN_mega_table_anim(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_reverse_table_anim: + + pars[0]=params[2]; //ob_logic + pars[1]=params[0]; //ob_graphic + pars[2]=params[3]; //ob_mega + pars[3]=ob_speech->ins1; //pointer to anim table + + ret= FN_reverse_mega_table_anim(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_no_sprite: + + FN_no_sprite(params); // ob_graphic; + + ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + + return(IR_REPEAT); + + //---------------------------------------------- + case INS_sort: + + FN_sort_sprite(params); // ob_graphic; + + ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + + return(IR_REPEAT); + + //---------------------------------------------- + case INS_foreground: + + FN_fore_sprite(params); // ob_graphic; + + ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + + return(IR_REPEAT); + + //---------------------------------------------- + case INS_background: + + FN_back_sprite(params); // ob_graphic; + + ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + + return(IR_REPEAT); + + //---------------------------------------------- + case INS_walk: + + pars[0]=params[2]; //ob_logic + pars[1]=params[0]; //ob_graphic + pars[2]=params[3]; //ob_mega + pars[3]=params[4]; //ob_walkdata + + pars[4]=ob_speech->ins1; //target x + pars[5]=ob_speech->ins2; //target y + pars[6]=ob_speech->ins3; //target direction + + ret= FN_walk(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command +// Zdebug("speech-process walk finished"); + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_walk_to_anim: + + pars[0]=params[2]; //ob_logic + pars[1]=params[0]; //ob_graphic + pars[2]=params[3]; //ob_mega + pars[3]=params[4]; //ob_walkdata + + pars[4]=ob_speech->ins1; // anim resource + + ret= FN_walk_to_anim(pars); + + if (ret!=IR_REPEAT) + { ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command +// Zdebug("speech-process walk finished"); + } + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_stand_after_anim: + + pars[0]=params[0]; // ob_graphic + pars[1]=params[3]; // ob_mega + pars[2]=ob_speech->ins1; // anim resource + + ret= FN_stand_after_anim(pars); + + ob_speech->command=0; // command finished + ob_speech->wait_state=1; // waiting for command + + return(IR_REPEAT); // come back again next cycle + + //---------------------------------------------- + case INS_set_frame: + + pars[0]=params[0]; // ob_graphic + pars[1]=ob_speech->ins1; // anim_resource + pars[2]=ob_speech->ins2; // FIRST_FRAME or LAST_FRAME + + ret= FN_set_frame(pars); + + ob_speech->command=0; //command finished + ob_speech->wait_state=1; //waiting for command + + return(IR_REPEAT); //come back again next cycle + + //---------------------------------------------- + case INS_quit: + +// Zdebug("speech-process - quit"); + ob_speech->command=0; //finish with all this + // ob_speech->wait_state=0; //start with waiting for command next conversation + return(IR_CONT); //thats it, we're finished with this + + //---------------------------------------------- + default: + + ob_speech->command=0; //not yet implemented - just cancel + ob_speech->wait_state=1; //waiting for command + break; + + //---------------------------------------------- + } + } + + + + if (SPEECH_ID==ID) //new command for us! + { + + SPEECH_ID=0; //clear this or it could trigger next go + +// grab the command - potentially, we only have this cycle to do this + ob_speech->command = INS_COMMAND; + ob_speech->ins1 = INS1; + ob_speech->ins2 = INS2; + ob_speech->ins3 = INS3; + ob_speech->ins4 = INS4; + ob_speech->ins5 = INS5; + + INS_COMMAND=0; //the current send has been recieved - i.e. seperate multiple they-do's + + ob_speech->wait_state=0; //now busy + +// Zdebug("received new command %d", INS_COMMAND); + +// we'll drop off and be caught by the while(1), so kicking in the new command straight away + + } + else //no new command + { +// we could run a blink anim (or something) here + + ob_speech->wait_state=1; //now free + return(IR_REPEAT); //come back again next cycle + } + } + + +} +//------------------------------------------------------------------------------------ + + + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +#if 1==2 + + + ooo l dddd sss ttttt u u fffff fffff +o o l d d s t u u f f +o o l d d sss t u u ffff ffff +o o l d d s t u u f f +o o l d d s s t u u f f +o o l d d s s t u u f f + ooo llll dddd sss t uuu f f + + + + +// ScriptReference 0,19 +mega_interact //standard interact protocol for megas who have been clicked on +{ //replaces S2's mega_sss... (Jan95tw) + + o_down_flag=0; //reset on entry - we dont know what this flag will be + //we will not be wiping a command because the player will + //not start until we signal - at the end of this loop... + //"smart" - readers voice + do + { + if (o_down_flag!=0) //recieved any instructions? + + switch(o_down_flag) + { + //-------------------------------------------- + case INS_talk: // (o_ins3=cdt or anim table, o_ins2=text id, o_ins1=graphic or 0) + { + FN_I_speak(o_ins3, o_ins2, o_ins1); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_anim: //run coord-less anim (o_ins1=cdt or anim table, o_ins2=graphic or 0, o_ins3=0) + { + FN_anim( o_ins1, o_ins2 ); + o_down_flag=0; + } + break; + + //-------------------------------------------- + + case INS_full_anim: //run anim with coords (o_ins1=cdt o_ins2=graphic o_ins3=0) + { + FN_full_anim( o_ins1, o_ins2 ); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_walk: // walk to (o_ins1,o_ins2) & stand in direction 'o_ins3' + { + FN_walk(o_ins1,o_ins2,o_ins3,STAND); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_turn: //turn/stand as specified + { + FN_turn(o_ins1,STAND); // turn to direction 'o_ins1' + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_face: //turn to face specified mega character + { + FN_face(o_ins1); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_trace: // face compact id 'o_ins1' - continues until next command (because 'o_down_flag' not reset to zero) + { + FN_face(o_ins1); + o_down_flag=INS_trace; // do this again & again until a new command is received + } + break; + + //-------------------------------------------- + case INS_no_sprite: + { + FN_no_sprite(); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_sort: + { + FN_sort(); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_foreground: + { + FN_foreground(); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_background: + { + FN_background(); + o_down_flag=0; + } + break; + + //-------------------------------------------- + case INS_quit: + { + } + break; //fall out of outer loop + + //-------------------------------------------- + default: + { + FN_talk_error(); //shut the system down - I can't be bothered to recover from this + } + break; + + //-------------------------------------------- + } + + + FN_add_talk_wait_status_bit(); // add waiting bit to status for player to pickup + FN_quit(); // drop out of script until next cycle to see if new instructions have arrived + FN_remove_talk_wait_status_bit(); // off again, in case command received + + } + while(o_down_flag!=INS_quit); //until we're told to terminate + + + FN_goto_bookmark(); //get the mega going again +} +//----------------------------------------------------------------------------------------- +// ScriptReference 0,20 + +mega_approach_script // George runs this script to get over to 'target_mega', who's already running 'mega_interact' +{ + + //FN_they_do_we_wait(target_mega,INS_trace,GEORGE,0,0); // 'target_mega' repeatedly faces george while he walks over + + FN_they_do_we_wait(target_mega,INS_face,GEORGE,0,0); // Mega turns to face George + + FN_get_pos(target_mega); + target_x = return_value; + target_y = return_value_2; + distance_apart = return_value_4; + + walk_attempt = 0; // keep count of attempts + + if ((o_xcoord) < target_x) // If George is currently left of Duane + walk_flag = 0; // try LHS first + else + walk_flag = 1; // try RHS first + + + //-------------------------------------------------------- + do + { + if (walk_flag==0) // (0) try LHS - & face DOWN_RIGHT + { + FN_walk(target_x-distance_apart,target_y,DOWN_RIGHT,STAND); + + if (o_down_flag) + walk_flag=2; // made it + else + walk_flag=1; // try RHS next, or fail (to anywhere) + } + + else // (1) try RHS & face DOWN_LEFT + { + FN_walk(target_x+distance_apart,target_y,DOWN_LEFT,STAND); + + if (o_down_flag) + walk_flag=2; // made it + else + walk_flag=0; // try LHS next, or fail (to anywhere) + } + + walk_attempt+=1; + } + while ((walk_flag<2)&&(walk_attempt<2)); // try again until we get there or we've tried both sides + //-------------------------------------------------------- + + + if (walk_flag!=2) // if LHS & RHS both failed - try anywhere + { + FN_walk(target_x,target_y,ANY,STAND); // walk George to (near) Mega's coords + } + + + FN_we_wait(target_mega); // wait for Duane to be ready to receive a command + + if ((o_xcoord) < target_x) // If George is now left of Duane + { + FN_they_do_we_wait(target_mega,INS_turn,DOWN_LEFT,0,0); // Mega faces DOWN_LEFT + FN_turn(DOWN_RIGHT, STAND); // George faces DOWN_RIGHT + } + else // George is now right of Duane + { + FN_they_do_we_wait(target_mega,INS_turn,DOWN_RIGHT,0,0); // Mega faces DOWN_LEFT + FN_turn(DOWN_LEFT, STAND); // George faces DOWN_RIGHT + } + //----------------------------------------------------------------------------------- +} +//------------------------------------------------------------------------------------ +int32 FN_they_do(object *compact, int32 id, int32 tar, int32 a, int32 b, int32 c, int32 d, int32 x) //S2.1(18Jan95tw) +{ + // NB. a, b & c could be resID's!! (25jul95 JEL) + + object *target; + + compact;id;x; + + //Tdebug("FN_they_do %d %d %d %d %d", tar,a,b,c,d); + + target = (object *) Lock_object(tar); + + target->o_down_flag = a; // the actual instruction; INS_talk, INS_quit, etc. + target->o_ins1 = b; // and now the 3 parameters... (updated 24apr95JEL) + target->o_ins2 = c; + target->o_ins3 = d; + + Unlock_object(tar); + + return(1); //script cont +} + +//-------------------------------------------------------------------------------------- + +//send an instruction to mega we're talking to and wait until it has finished before +//returning to script + +int32 FN_they_do_we_wait(object *compact, int32 id, int32 tar, int32 a, int32 b, int32 c, int32 d, int32 x) +{ //S2.1(18Jan95tw) + + // NB. a, b & c could be resID's!! (25jul95 JEL) + + object *target; + + compact;id;x; + + //Tdebug("FN_they_do_we_wait %d %d %d %d %d", tar,a,b,c,d); + + target = (object *) Lock_object(tar); + + target->o_down_flag = a; // the actual instruction; INS_talk, INS_quit, etc. + target->o_ins1 = b; // and now the 3 parameters... (updated 24apr95JEL) + target->o_ins2 = c; + target->o_ins3 = d; + + compact->o_logic = LOGIC_wait_for_talk; // we wait until they've finished + compact->o_down_flag=tar; // we wait for this person + + target->o_status &= (0xffffffff-STAT_TALK_WAIT); // remove for this cycle - remember, this damn system is totally seamless... + + Unlock_object(tar); + return(0); //script stop +} + +//-------------------------------------------------------------------------------------- +// wait until last instruction it has finished before returning to script + +int32 FN_we_wait(object *compact, int32 id, int32 tar, int32 a, int32 b, int32 c, int32 d, int32 e) // (7JULY95 JEL) +{ + object *target; + + compact;id;a;b;c;d;e; + + target = (object *) Lock_object(tar); + + //Tdebug("FN_we_wait %d", tar); + + compact->o_logic = LOGIC_wait_for_talk; // we wait until they've finished + compact->o_down_flag=tar; // we wait for this person + + target->o_status &= (0xffffffff-STAT_TALK_WAIT); // remove for this cycle - remember, this damn system is totally seamless... + + Unlock_object(tar); + return(0); //script stop +} + +//-------------------------------------------------------------------------------------- +int32 FN_add_talk_wait_status_bit(object *compact, int32 id, int32 tar, int32 a, int32 b, int32 c, int32 d, int32 e) // (21may96 JEL) +{ + id;tar;a;b;c;d;e; + + compact->o_status |= STAT_TALK_WAIT; + + return(1); //script continue +} +//-------------------------------------------------------------------------------------- +int32 FN_remove_talk_wait_status_bit(object *compact, int32 id, int32 tar, int32 a, int32 b, int32 c, int32 d, int32 e) // (21may96 JEL) +{ + id;tar;a;b;c;d;e; + + compact->o_status &= (0xffffffff-STAT_TALK_WAIT); + + return(1); //script continue +} +//-------------------------------------------------------------------------------------- + +//mega_interact has recieved an instruction it does not understand - I have chosen to +//halt the game to ensure the error is noticed... + +int32 FN_talk_error(object *compact, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) //S2.1(18Jan95tw) +{ + compact;id;a;b;c;d;z;x; + + Tdebug("FN_talk_error for %d - instruct = %d",id, compact->o_down_flag); + Go_dos("FN_talk_error for %d - instruct = %d",id, compact->o_down_flag); + + return(0); +} + +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- + +//wait for target to switch into STAT_TALK_WAIT then go back into script mode +//used during speech they_do commands - we must look to a specific target in-case +//of multiple particpents in the conversation - a sync from the target is not enough... + +Logic_wait_talk(object *compact, int id) //S2.1(25Jan95tw) +{ + object *target; + + id; + + target = (object *) Lock_object(compact->o_down_flag); //holds id of person we're waiting for + + + if (!(target->o_status&STAT_TALK_WAIT)) + { + Unlock_object(compact->o_down_flag); //holds id of person we're waiting for + return(0); //0 which means drop out of logic + } + else + { + compact->o_logic=LOGIC_script; //back to script again next cycle + Unlock_object(compact->o_down_flag); //holds id of person we're waiting for + return(1); // go straight back into script + //return(0); // drop out for one cycle (allows speech text sprite to disappear before continuing - 13Mar95JEL) + } +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +#endif + + + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +uint32 unpause_zone=0; +//------------------------------------------------------------------------------------ +int32 FN_i_speak(int32 *params) //Tony18Oct96 (revamped by James01july97) +{ +//its the super versatile FN_speak +//text and wavs can be selected in any combination + +//we can assume no human - there should be no human at least! + +//param 0 pointer to ob_graphic +//param 1 pointer to ob_speech +//param 2 pointer to ob_logic +//param 3 pointer to ob_mega + +//param 4 encoded text number +//param 5 wav res id +//param 6 anim res id +//param 7 anim table res id +//param 8 animation mode 0 lip synced, 1 just straight animation + +#define S_OB_GRAPHIC 0 +#define S_OB_SPEECH 1 +#define S_OB_LOGIC 2 +#define S_OB_MEGA 3 + +#define S_TEXT 4 +#define S_WAV 5 +#define S_ANIM 6 +#define S_DIR_TABLE 7 +#define S_ANIM_MODE 8 + + _mouseEvent *me; + _animHeader *anim_head; + Object_logic *ob_logic; + Object_graphic *ob_graphic; + Object_mega *ob_mega; + uint8 *anim_file; + uint32 local_text; + uint32 text_res; + uint8 *text; + static uint8 textRunning, speechRunning; + int32 *anim_table; + uint8 speechFinished=0; // James25feb97 + int8 speech_pan; + char speechFile[256]; + static uint8 cycle_skip=0; + + #ifdef _DEBUG // (James26jun97) + _standardHeader *head; // for text/speech testing & checking for correct file type + static uint32 currentTextResource=0; // for text/speech testing - keeping track of text resource currently being tested + #endif + + uint32 rv; // drivers return value + + + //----------------------------------------------- + // set up the pointers which we know we'll always need + + ob_logic = (Object_logic *) params[S_OB_LOGIC]; + ob_graphic = (Object_graphic *) params[S_OB_GRAPHIC]; + + //----------------------------------------------- + // FIRST TIME ONLY: create the text, load the wav, set up the anim, etc. + + if (!ob_logic->looping) + { + //------------------------- + // New fudge to wait for smacker samples to finish (James31july97) + // since they can over-run into the game + + if (GetSpeechStatus()!=RDSE_SAMPLEFINISHED) // has it finished? + return (IR_REPEAT); + + //------------------------- + // New fudge for 'fx' subtitles (James 29july97) + // If subtitles switched off, and we don't want to use a wav for this line either, + // then just quit back to script right now! + if ((subtitles==0) && (WantSpeechForLine(params[S_WAV])==0)) + return (IR_CONT); + + //------------------------- + if (cycle_skip==0) // (James 17july97) + { + // drop out for 1st cycle to allow walks/anims to end & display last frame + // before system locks while speech loaded + cycle_skip=1; + return (IR_REPEAT); + } + else + cycle_skip=0; + //------------------------- + + //----------------------------------------------------------- + #ifdef _DEBUG // (James26jun97) + + textNumber = params[S_TEXT]; // for debug info + + // For testing all text & speech! (James26jun97) + // A script loop can send any text number to FN_I_speak & it will only run the valid ones + // or return with 'result' equal to '1' or '2' to mean 'invalid text resource' + // and 'text number out of range' respectively + // See 'testing_routines' object in George's Player Character section of linc + + if (SYSTEM_TESTING_TEXT) + { + RESULT=0; + + text_res = params[S_TEXT]/SIZE; + local_text = params[S_TEXT]&0xffff; + + if (res_man.Res_check_valid(text_res)) // if the resource number is within range & it's not a null resource + { + head = (_standardHeader*) res_man.Res_open(text_res); // open the resource + + if (head->fileType==TEXT_FILE) // if it's not an animation file + { + if (CheckTextLine((uint8*)head,local_text)==0) // if line number is out of range + RESULT=2; // line number out of range + } + else + RESULT=1; // invalid (not a text resource) + + res_man.Res_close(text_res); // close the resource + + if (RESULT) + return(IR_CONT); + } + else + { // not a valid resource number + RESULT=1; // invalid (null resource) + return(IR_CONT); + } + } + + #endif // _DEBUG + //----------------------------------------------------------- + // pull out the text line to get the official text number (for wav id) + // Once the wav id's go into all script text commands, we'll only need this for _DEBUG + + text_res = params[S_TEXT]/SIZE; + local_text = params[S_TEXT]&0xffff; + + text = FetchTextLine( res_man.Res_open(text_res), local_text ); // open text file & get the line + //officialTextNumber = *(uint16*)text; // 1st word of text line is the official line number (this doesn't work on PSX) + memcpy(&officialTextNumber, text, 2); // this works on PSX & PC + + res_man.Res_close(text_res); // now ok to close the text file + + //-------------------------------------- + #ifdef _DEBUG // (James09jul97) + + // prevent dud lines from appearing while testing text & speech + // since these will not occur in the game anyway + + if (SYSTEM_TESTING_TEXT) // if testing text & speech + { // if actor number is 0 and text line is just a 'dash' character + if ((officialTextNumber==0) && (text[2]=='-') && (text[3]==NULL)) + { + RESULT=3; // dud line + return(IR_CONT); // return & continue script + } + } + + #endif // _DEBUG + //-------------------------------------- + // set the 'looping_flag' & the text-click-delay + + ob_logic->looping=1; + left_click_delay=6; // can't left-click past the text for the first half second + right_click_delay=3; // can't right-click past the text for the first quarter second + + //---------------------------------------------------------- + // Write to walkthrough file (zebug0.txt) + + #ifdef _DEBUG + if (PLAYER_ID!=CUR_PLAYER_ID) // if (player_id != george), then player is controlling Nico + Zdebug(0,"(%d) Nico: %s", officialTextNumber, text+2); // so write 'Nico' instead of George + else // ok, it's George anyway + Zdebug(0,"(%d) %s: %s", officialTextNumber, FetchObjectName(ID), text+2); + #endif + + //-------------------------------------- + // Set up the speech animation + + if (params[S_ANIM]) //just a straight anim + { + anim_id=params[S_ANIM]; + speech_anim_type=SPEECHANIMFLAG; //params[S_ANIM_MODE]; //anim type + + ob_graphic->anim_resource = anim_id; // set the talker's graphic to this speech anim now + ob_graphic->anim_pc = 0; // set to first frame + } + else if (params[S_DIR_TABLE]) //use this direction table to derive the anim NB. ASSUMES WE HAVE A MEGA OBJECT!! + { + ob_mega = (Object_mega*) params[S_OB_MEGA]; + + anim_table = (int32 *)params[S_DIR_TABLE]; // pointer to anim table + anim_id = anim_table[ob_mega->current_dir]; // appropriate anim resource is in 'table[direction]' + + speech_anim_type=SPEECHANIMFLAG; //params[S_ANIM_MODE]; //anim type + + ob_graphic->anim_resource = anim_id; // set the talker's graphic to this speech anim now + ob_graphic->anim_pc = 0; // set to first frame + } + else //no animation choosen + { + anim_id=0; //no animation + } + + SPEECHANIMFLAG = 0; // Default back to looped lip synced anims. + + //-------------------------------------- + // set up 'text_x' & 'text_y' for speech-pan and/or text-sprite position + + LocateTalker(params); + + //-------------------------------------- + // is it to be speech or subtitles or both? + + speechRunning=0; // assume not running until know otherwise + + // New fudge for 'fx' subtitles (James 29july97) + // if speech is selected, and this line is allowed speech (not if it's an fx subtitle!) + if (speechSelected && WantSpeechForLine(officialTextNumber)) + { + // if the wavId paramter is zero because not yet compiled into speech command, + // we can still get it from the 1st 2 chars of the text line + if (!params[S_WAV]) + params[S_WAV] = (int32)officialTextNumber; + + #define SPEECH_VOLUME 16 // 0..16 + #define SPEECH_PAN 0 // -16..16 + + speech_pan = ((text_x-320)*16)/320; + // 'text_x' 'speech_pan' + // 0 -16 + // 320 0 + // 640 16 + + if (speech_pan<-16) // keep within limits of -16..16, just in case + speech_pan=-16; + else if (speech_pan>16) + speech_pan=16; + + #ifdef _DEBUG + if (SYSTEM_TESTING_TEXT) // if we're testing text & speech + { + // if we've moved onto a new text resource, we will want to check + // if the CD needs changing again + // - can only know which CD to get if the wavID is non-zero + if ((text_res != currentTextResource) && (params[S_WAV])) + { + GetCorrectCdForSpeech(params[S_WAV]); // ensure correct CD is in for this wavId + currentTextResource = text_res; + } + } + #endif + + //------------------------------ + // set up path to speech cluster + // first checking if we have speech1.clu or speech2.clu in current directory (for translators to test) + +#ifdef _WEBDEMO // (James 01oct97) + strcpy(speechFile,"SPEECH.CLU"); +#else + + +#ifdef _DEBUG + if ((res_man.WhichCd()==1) && (!access("speech1.clu",0))) // if 0 ie. if it's there + { + strcpy(speechFile,"speech1.clu"); + } + else if ((res_man.WhichCd()==2) && (!access("speech2.clu",0))) // if 0 ie. if it's there + { + strcpy(speechFile,"speech2.clu"); + } + else +#endif // _DEBUG + { + strcpy(speechFile,res_man.GetCdPath()); + strcat(speechFile,"CLUSTERS\\SPEECH.CLU"); + } +#endif // _WEBDEMO + //------------------------------ + + + rv = PlayCompSpeech(speechFile, params[S_WAV], SPEECH_VOLUME, speech_pan); // Load speech but don't start playing yet + if (rv == RD_OK) + { + speechRunning=1; // ok, we've got something to play (2 means not playing yet - see below) + UnpauseSpeech(); // set it playing now (we might want to do this next cycle, don't know yet) + } + #ifdef _DEBUG + else + { + Zdebug("ERROR: PlayCompSpeech(speechFile=\"%s\", wav=%d (res=%d pos=%d)) returned %.8x", speechFile, params[S_WAV], text_res, local_text, rv); + } + #endif + + + } + + if (subtitles || (speechRunning==0)) // if we want subtitles, or speech failed to load + { + textRunning=1; // then we're going to show the text + Form_text(params); // so create the text sprite + } + else + textRunning=0; // otherwise don't want text + + //-------------------------------------- + + } + //----------------------------------------------- + // EVERY TIME: run a cycle of animation, if there is one + + if (anim_id) // there is an animation + { + ob_graphic->anim_pc++; // increment the anim frame number + + anim_file = res_man.Res_open(ob_graphic->anim_resource); // open the anim file + anim_head = FetchAnimHeader( anim_file ); + + if (!speech_anim_type) // ANIM IS TO BE LIP-SYNC'ED & REPEATING + { + if (ob_graphic->anim_pc == (int32)(anim_head->noAnimFrames)) // if finished the anim + ob_graphic->anim_pc=0; // restart from frame 0 + else if (speechRunning) // if playing a sample + { + if (!unpause_zone) + { if (AmISpeaking()==RDSE_QUIET) // if we're at a quiet bit + ob_graphic->anim_pc=0; // restart from frame 0 ('closed mouth' frame) + } + } + } + else // ANIM IS TO PLAY ONCE ONLY + { + if (ob_graphic->anim_pc == (int32)(anim_head->noAnimFrames)-1) // reached the last frame of the anim + { + anim_id=0; // hold anim on this last frame + } + } + + res_man.Res_close(ob_graphic->anim_resource); // close the anim file + } + else if (speech_anim_type) + speech_anim_type = 0; // Placed here so we actually display the last frame of the anim. + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + // EVERY TIME: FIND OUT IF WE NEED TO STOP THE SPEECH NOW... + //----------------------------------------------------------------------- + // if there is a wav then we're using that to end the speech naturally + + if (speechRunning==1) // if playing a sample (note that value of '2' means about to play!) + { + if (!unpause_zone) + { if (GetSpeechStatus()==RDSE_SAMPLEFINISHED) // has it finished? + speechFinished=1; // James25feb97 + } + else unpause_zone--; + } + //----------------------------------------------------------------------- + // if no sample then we're using speech_time to end speech naturally + + else if ((speechRunning==0)&&(speech_time)) // counting down text time because there is no sample - this ends the speech + { + speech_time--; + if (!speech_time) + speechFinished=1; // James25feb97 + } + //----------------------------------------------------------------------- + // ok, all is running along smoothly - but a click means stop unnaturally + + #ifdef _DEBUG + if ((SYSTEM_TESTING_TEXT==0)||(mousey>0)) // so that we can go to the options panel while text & speech is being tested + #endif + { + me = MouseEvent(); // get mouse event + + // Note that we now have TWO click-delays - one for LEFT button, one for RIGHT BUTTON + if ( ((!left_click_delay)&&(me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) || ((!right_click_delay)&&(me!=NULL)&&(me->buttons&RD_RIGHTBUTTONDOWN)) ) + { + // mouse click, after click_delay has expired -> end the speech + // we ignore mouse releases + + //----------------------------------------------------------- + #ifdef _DEBUG // (James26jun97) + if (SYSTEM_TESTING_TEXT) // if testing text & speech + { + if (me->buttons&RD_RIGHTBUTTONDOWN) // and RB used to click past text + SYSTEM_WANT_PREVIOUS_LINE=1; // then we want the previous line again + else + SYSTEM_WANT_PREVIOUS_LINE=0; // LB just want next line again + } + #endif + //----------------------------------------------------------- + + do + me = MouseEvent(); //trash anything thats buffered + while(me!=NULL); + + speechFinished=1; // James25feb97 + + if (speechRunning) // if speech sample playing + { + StopSpeech(); // halt the sample prematurely + } + } + } + //----------------------------------------------------------------------- + // if we are finishing the speech this cycle, do the business + + if (speechFinished && !speech_anim_type) // !speech_anim_type, as we want an anim which is playing once to have finished. + { + if (speech_text_bloc_no) // if there is text + { + Kill_text_bloc(speech_text_bloc_no); // kill the text block + speech_text_bloc_no=0; + } + + if (anim_id) // if there is a speech anim + { + anim_id=0; + ob_graphic->anim_pc=0; // end it on 1st frame (closed mouth) + } + + textRunning=0; + speechRunning=0; + + ob_logic->looping=0; // no longer in a script function loop + +#ifdef _DEBUG + textNumber = 0; // reset for debug info +#endif // _DEBUG + officialTextNumber = 0; // reset to zero, in case text line not even extracted (since this number comes from the text line) + + RESULT=0; // ok (James09july97) + return(IR_CONT); // continue with the script + } + //----------------------------------------------------------------------- + // speech still going, so decrement the click_delay if it's still active + + if (left_click_delay) + left_click_delay--; // count down to clickability + + if (right_click_delay) + right_click_delay--; // count down to clickability + + //----------------------------------------------------------------------- + + return(IR_REPEAT); // back again next cycle + +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void LocateTalker(int32 *params) // (James 01july97) +{ + // sets 'text_x' & 'text_y' for position of text sprite + // but 'text_x' also used to calculate speech-pan + + //param 0 pointer to ob_graphic + //param 1 pointer to ob_speech + //param 2 pointer to ob_logic + //param 3 pointer to ob_mega + + //param 4 encoded text number + //param 5 wav res id + //param 6 anim res id + //param 7 pointer to anim table + //param 8 animation mode 0 lip synced, 1 just straight animation + + Object_mega *ob_mega; + + uint8 *file; + _frameHeader *frame_head; + _animHeader *anim_head; + _cdtEntry *cdt_entry; + uint16 scale; + + + + + if (anim_id==0) // if there's no anim + { // assume it's Voice-Over text + text_x = 320; // so it goes at bottom of screen + text_y = 400; + } + else + { + #define GAP_ABOVE_HEAD 20 // distance kept above talking sprite + + //------------------------------------------- + // Note: this code has been adapted from Register_frame() in build_display.cpp + //------------------------------------------- + // open animation file & set up the necessary pointers + + file = res_man.Res_open(anim_id); + + anim_head = FetchAnimHeader( file ); + cdt_entry = FetchCdtEntry( file, 0 ); // '0' means 1st frame + frame_head = FetchFrameHeader( file, 0 ); // '0' means 1st frame + + //------------------------------------------- + // check if this frame has offsets ie. this is a scalable mega frame + + if ((cdt_entry->frameType) & FRAME_OFFSET) + { + ob_mega = (Object_mega*) params[S_OB_MEGA]; // this may be NULL + + // calc scale at which to print the sprite, based on feet y-coord & scaling constants (NB. 'scale' is actually 256*true_scale, to maintain accuracy) + scale = (ob_mega->scale_a * ob_mega->feet_y + ob_mega->scale_b)/256; // Ay+B gives 256*scale ie. 256*256*true_scale for even better accuracy, ie. scale = (Ay+B)/256 + + // calc suitable centre point above the head, based on scaled height + text_x = ob_mega->feet_x; // just use 'feet_x' as centre + text_y = ob_mega->feet_y + (cdt_entry->y * scale)/256; // add scaled y-offset to feet_y coord to get top of sprite + } + else // it's a non-scaling anim + { + // calc suitable centre point above the head, based on scaled width + text_x = cdt_entry->x + (frame_head->width)/2; // x-coord + half of width + text_y = cdt_entry->y; // y-coord + } + + text_y -= GAP_ABOVE_HEAD; // leave space above their head + + //------------------------------------------- + // adjust the text coords for RDSPR_DISPLAYALIGN + + text_x -= this_screen.scroll_offset_x; + text_y -= this_screen.scroll_offset_y; + + //------------------------------------------- + // release the anim resource + + res_man.Res_close(anim_id); + + //------------------------------------------- + } +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void Form_text(int32 *params) //Tony18Oct96 +{ +//its the first time in so we build the text block if we need one +//we also bring in the wav if there is one +//also setup the animation if there is one + +//anim is optional - anim can be a repeating lip-sync or a run-once anim +//if there is no wav then the text comes up instead +//there can be any combination of text/wav playing + +//param 0 pointer to ob_graphic +//param 1 pointer to ob_speech +//param 2 pointer to ob_logic +//param 3 pointer to ob_mega + +//param 4 encoded text number +//param 5 wav res id +//param 6 anim res id +//param 7 pointer to anim table +//param 8 animation mode 0 lip synced, 1 just straight animation + + + uint32 local_text; + uint32 text_res; + uint8 *text; + uint32 textWidth; + Object_speech *ob_speech; + + + + if (params[S_TEXT]) //should always be a text line, as all text is derived from line of text + { + ob_speech = (Object_speech*) params[S_OB_SPEECH]; + + //---------------------------------------------------------- + // establish the max width allowed for this text sprite + + if (ob_speech->width) // if a specific width has been set up for this character + textWidth = ob_speech->width; // override the default + else + textWidth = 400; // otherwise use the default + + //---------------------------------------------------------- + // pull out the text line & make the sprite & text block + + text_res = params[S_TEXT]/SIZE; + local_text = params[S_TEXT]&0xffff; + + text = FetchTextLine( res_man.Res_open(text_res), local_text ); // open text file & get the line + + // 'text+2' to skip the first 2 bytes which form the line reference number + speech_text_bloc_no = Build_new_block(text+2, text_x, text_y, textWidth, ob_speech->pen, RDSPR_TRANS+RDSPR_DISPLAYALIGN, speech_font_id, POSITION_AT_CENTRE_OF_BASE); + + res_man.Res_close(text_res); // now ok to close the text file + + //---------------------------------------------------------- + // set speech duration, in case not using wav + + speech_time = strlen((char *)text) + 30; // no. of cycles = (no. of chars) + 10 + + //---------------------------------------------------------- + } + else // no text line passed? - this is bad + { + // Zdebug(9,"no text line for speech wav %d", params[S_WAV]); //stream 9 for missing text & wavs + Zdebug("no text line for speech wav %d", params[S_WAV]); //stream 9 for missing text & wavs + } +} +//------------------------------------------------------------------------------------ +#ifdef _DEBUG + +void GetCorrectCdForSpeech(int32 wavId) +{ + FILE *fp; + uint8 cd; // 1, 2 or 0 (if speech on both cd's, ie. no need to change) + + fp = fopen("cd.bin","rb"); + + if (fp==NULL) + Con_fatal_error("Need cd.bin file for testing speech!"); + + fseek(fp, wavId, SEEK_SET); + fread(&cd, 1, 1, fp); + + fclose(fp); + + if ((cd==1)||(cd==2)) // if we specifically need CD1 or CD2 (ie. it's not on both) + res_man.GetCd(cd); // then check it's there (& ask for it if it's not there) +} + +#endif +//------------------------------------------------------------------------------------ +// For preventing sfx subtitles from trying to load speech samples +// - since the sfx are implemented as normal sfx, so we don't want them as speech samples too +// - and we only want the subtitles if selected, not if samples can't be found! + +uint8 WantSpeechForLine(uint32 wavId) // James (29july97) +{ + switch (wavId) + { + case 1328: // AttendantSpeech + // SFX(Phone71); + // FX <Telephone rings> + + case 2059: // PabloSpeech + // SFX (2059); + // FX <Sound of sporadic gunfire from below> + + case 4082: // DuaneSpeech + // SFX (4082); + // FX <Pffffffffffft! Frp. (Unimpressive, flatulent noise.)> + + case 4214: // cat_52 + // SFX (4214); + // 4214FXMeow! + + case 4568: // trapdoor_13 + // SFX (4568); + // 4568fx<door slamming> + + case 4913: // LobineauSpeech + // SFX (tone2); + // FX <Lobineau hangs up> + + case 5120: // bush_66 + // SFX (5120); + // 5120FX<loud buzzing> + + case 528: // PresidentaSpeech + // SFX (528); + // FX <Nearby Crash of Collapsing Masonry> + + case 920: // location 62 + + case 923: // location 62 + + case 926: // location 62 + + { + return 0; // don't want speech for these lines! + break; + } + + default: + return 1; // ok for all other lines + } +} +//------------------------------------------------------------------------------------ diff --git a/sword2/speech.h b/sword2/speech.h new file mode 100644 index 0000000000..3630f56110 --- /dev/null +++ b/sword2/speech.h @@ -0,0 +1,44 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _SPEECH +#define _SPEECH + +//#include "src\driver96.h" +#include "header.h" + + +#define MAX_SUBJECT_LIST 30 //is that enough? + + + +typedef struct //array of these for subject menu build up +{ + uint32 res; + uint32 ref; +} _subject_unit; + +extern uint32 speech_text_bloc_no; // so speech text cleared when running a new start-script +extern int16 officialTextNumber; + +extern int32 speechScriptWaiting; + +extern int choosing; //could alternately use logic->looping of course +extern uint32 unpause_zone; +#endif diff --git a/sword2/startup.cpp b/sword2/startup.cpp new file mode 100644 index 0000000000..0096a389e2 --- /dev/null +++ b/sword2/startup.cpp @@ -0,0 +1,339 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +#include <stdio.h> + +//#include "src\driver96.h" +#include "build_display.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "header.h" +#include "interpreter.h" +#include "maketext.h" // for Kill_text_bloc() +#include "memory.h" +#include "mouse.h" // for FN_add_human() +#include "object.h" +#include "resman.h" +#include "router.h" +#include "sound.h" +#include "speech.h" // for 'speech_text_bloc_no' - so that speech text can be cleared when running a new start-script +#include "startup.h" +#include "sword2.h" // (James11aug97) for CloseGame() +#include "sync.h" +#include "tony_gsdk.h" + +//------------------------------------------------------------------------------------ +uint32 total_startups=0; +uint32 total_screen_managers=0; +uint32 res; + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +_startup start_list[MAX_starts]; +//------------------------------------------------------------------------------------ +uint32 Init_start_menu(void) //Tony13Aug96 +{ +//print out a list of all the start points available +//there should be a linc produced file called startup.txt +//this file should contain ascii numbers of all the resource game objects that are screen managers +//we query each in turn and setup an array of start structures +//if the file doesn't exist then we say so and return a 0 + + + uint32 end; + mem *temp; + uint32 pos=0; + uint32 j=0; + char *raw_script; + uint32 null_pc; + + + char ascii_start_ids[MAX_starts][7]; + + +//ok, load in the master screen manager file + + + + + total_startups=0; //no starts + + + + Zdebug("initialising start menu"); + + + if (!(end=Read_file("STARTUP.INF", &temp, UID_temp))) + { + Zdebug("Init_start_menu cannot open startup.inf"); + return(0); //meaning no start menu available + } + + + +//ok, we've loaded in the resource.inf file which contains a list of all the files +//now extract the filenames + do + { + while(((char)*(temp->ad+j))!=13) //item must have an #0d0a + { + ascii_start_ids[total_screen_managers][pos]=*(temp->ad+j); + j++; + pos++; + + }; + + ascii_start_ids[total_screen_managers][pos]=0; //NULL terminate our extracted string + + pos=0; //reset position in current slot between entries + j+=2; //past the 0a + total_screen_managers++; //done another + + if (total_screen_managers==MAX_starts) + { Zdebug("WARNING MAX_starts exceeded!"); + break; + } + } + while(j<end); //using this method the Gode generated resource.inf must have #0d0a on the last entry + + + + Zdebug("%d screen manager objects", total_screen_managers); + +//open each object and make a query call. the object must fill in a startup structure +//it may fill in several if it wishes - for instance a startup could be set for later in the game where specific vars are set + for (j=0;j<total_screen_managers;j++) + { + res=atoi(ascii_start_ids[j]); + + Zdebug("+querying screen manager %d", res); + + +// resopen each one and run through the interpretter +// script 0 is the query request script + + + if (res_man.Res_check_valid(res)) // if the resource number is within range & it's not a null resource (James 12mar97) + { // - need to check in case un-built sections included in start list + Zdebug("- resource %d ok",res); + raw_script= (char*) (res_man.Res_open(res)); //+sizeof(_standardHeader)+sizeof(_object_hub)); + null_pc=0; // + RunScript ( raw_script, raw_script, &null_pc ); + res_man.Res_close(res); + } + else + { + Zdebug("- resource %d invalid",res); + } + } + + + Zdebug(""); //line feed + + + Free_mem(temp); //release the Talloc + + return(1); +} +//------------------------------------------------------------------------------------ +int32 FN_register_start_point(int32 *params) //Tony14Oct96 +{ + // param 0 id of startup script to call - key + // param 1 pointer to ascii message + + +// Zdebug(" FN_register_start_point %d %s", params[0], params[1]); + + #ifdef _DEBUG + if (total_startups==MAX_starts) + Con_fatal_error("ERROR: start_list full [%s line %u]",__FILE__,__LINE__); + + if (strlen((char*)params[1])+1 > MAX_description) // +1 to allow for NULL terminator + Con_fatal_error("ERROR: startup description too long [%s line %u]",__FILE__,__LINE__); + #endif + + start_list[total_startups].start_res_id = res; // this objects id + start_list[total_startups].key = params[0]; // a key code to be passed to a script via a script var to SWITCH in the correct start + strcpy(start_list[total_startups].description, (char*)params[1]); + + total_startups++; //point to next + + return(1); +} +//------------------------------------------------------------------------------------ +uint32 Con_print_start_menu(void) //Tony14Oct96 +{ + //the console 'starts' (or 's') command which lists out all the registered start points in the game + uint32 j; + int scrolls=0; + char c; + + + if (!total_startups) + { Print_to_console("Sorry - no startup positions registered?"); + + if (!total_screen_managers) + Print_to_console("There is a problem with startup.inf"); + else + Print_to_console(" (%d screen managers found in startup.inf)", total_screen_managers); + + } + else + { + for(j=0;j<total_startups;j++) + { + Print_to_console("%d (%s)", j, start_list[j].description); + Build_display(); + scrolls++; + + + if (scrolls==18) + { + Temp_print_to_console("- Press ESC to stop or any other key to continue"); + Build_display(); + + do + { + //-------------------------------------------------- + // Service windows + while (!gotTheFocus) + if (ServiceWindows() == RDERR_APPCLOSED) + break; + + if (ServiceWindows() == RDERR_APPCLOSED) // if we pressed Ctrl-Q + { + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + exit(0); //quit the game + } + //-------------------------------------------------- + } + while(!KeyWaiting()); + + ReadKey(&c); //kill the key we just pressed + if (c==27) //ESC + break; + + Clear_console_line(); //clear the Press Esc message ready for the new line + scrolls=0; + } + + + } + } + return(1); +} +//------------------------------------------------------------------------------------ +uint32 Con_start(uint8 *input) //Tony15Oct96 +{ +//if the second word id is a numeric that can be applied to a genuine startup then do it + uint32 j=0; + uint32 start; + char *raw_script; + char *raw_data_ad; + uint32 null_pc; + + + + if (*input == NULL) // so that typing 'S' then <enter> works on NT (James26feb97) + { + Con_print_start_menu(); + return(1); + } + + + while(*(input+j)) + { + if ( (*(input+j)>='0') && (*(input+j)<='9') ) + j++; + else + break; + } + + + if (!*(input+j)) //didn't quit out of loop on a non numeric chr$ + { + start = atoi((char*)input); + + if (!total_startups) + Print_to_console("Sorry - there are no startups!"); + + else if (start<total_startups) //a legal start + { +// do the startup as we've specified a legal start + + //-------------------------------------------------------------- + // restarting - stop sfx, music & speech! + + Clear_fx_queue(); + //--------------------------------------------- + FN_stop_music(NULL); // fade out any music that is currently playing + //--------------------------------------------- + + UnpauseSpeech(); + StopSpeech(); // halt the sample prematurely + + //-------------------------------------------------------------- + // clean out all resources & flags, ready for a total restart (James24mar97) + + res_man.Remove_all_res(); // remove all resources from memory, including player object & global variables + SetGlobalInterpreterVariables((int32*)(res_man.Res_open(1)+sizeof(_standardHeader))); // reopen global variables resource & send address to interpreter - it won't be moving + res_man.Res_close(1); + + FreeAllRouteMem(); // free all the route memory blocks from previous game + + if (speech_text_bloc_no) // if there was speech text + { + Kill_text_bloc(speech_text_bloc_no); // kill the text block + speech_text_bloc_no=0; + } + + //-------------------------------------------------------------- + +// set the key + raw_data_ad= (char*) (res_man.Res_open(8)); //+sizeof(_standardHeader)+sizeof(_object_hub)); //open george + raw_script= (char*) (res_man.Res_open(start_list[start].start_res_id)); //+sizeof(_standardHeader)+sizeof(_object_hub)); + null_pc=start_list[start].key&0xffff; //denotes script to run + Print_to_console("running start %d", start); + RunScript ( raw_script, raw_data_ad, &null_pc ); + + res_man.Res_close(start_list[start].start_res_id); + res_man.Res_close(8); //close george + + FN_add_human(NULL); // make sure thre's a mouse, in case restarting while mouse not available + } + else + Print_to_console("not a legal start position"); + } + else + { + Con_print_start_menu(); // so that typing 'S' then <enter> works under Win95 + } + return(1); +} + +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + + diff --git a/sword2/startup.h b/sword2/startup.h new file mode 100644 index 0000000000..da69389164 --- /dev/null +++ b/sword2/startup.h @@ -0,0 +1,45 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _STARTUP +#define _STARTUP + +//#include "src\driver96.h" + + +#define MAX_starts 100 +#define MAX_description 100 + + +typedef struct +{ + char description[MAX_description]; + uint32 start_res_id; //id of screen manager object + uint32 key; //tell the manager which startup you want (if there are more than 1) (i.e more than 1 entrance to a screen and/or seperate game boots) + +} _startup; + + +extern _startup start_list[MAX_starts]; + +uint32 Init_start_menu(void); //Tony13Aug96 +uint32 Con_print_start_menu(void); //Tony13Aug96 +uint32 Con_start(uint8 *input); //Tony15Oct96 + +#endif diff --git a/sword2/sword2.cpp b/sword2/sword2.cpp new file mode 100644 index 0000000000..c12d7bb09f --- /dev/null +++ b/sword2/sword2.cpp @@ -0,0 +1,572 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +#include <ctype.h> +#include <stdio.h> +//#include <windows.h> + +#include "driver/driver96.h" +#include "common/gameDetector.h" +#include "build_display.h" +#include "console.h" +#include "controls.h" +#include "debug.h" +#include "events.h" +#include "header.h" +#include "interpreter.h" +#include "layers.h" +#include "logic.h" +#include "maketext.h" +#include "memory.h" +#include "mouse.h" +#include "protocol.h" +#include "resman.h" +#include "scroll.h" +#include "sound.h" +#include "speech.h" +#include "startup.h" +#include "sword2.h" +#include "sync.h" + +#define MAX_PATH 260 + +void Start_game(void); + +int RunningFromCd(); + +uint8 quitGame = 0; + +//------------------------------------------------------------------------------------ +// version & owner details + +//So version string is 18 bytes long : +//Version String = <8 byte header,5 character version, \0, INT32 time> + +uint8 version_string[HEAD_LEN+10] = {1, 255, 37, 22, 45, 128, 34, 67}; + +uint8 unencoded_name[HEAD_LEN+48] = {76, 185, 205, 23, 44, 34, 24, 34, + 'R','e','v','o','l','u','t','i','o','n',' ', + 'S','o','f','t','w','a','r','e',' ','L','t','d', + 0}; + +uint8 encoded_name[HEAD_LEN+48] = {44, 32, 190, 222, 123, 65, 233, 99, + 179, 209, 225, 157, 222, 238, 219, 209, 143, 224, 133, 190, + 232, 209, 162, 177, 198, 228, 202, 146, 180, 232, 214, 65, + 65, 65, 116, 104, 116, 114, 107, 104, 32, 49, 64, 35, 123, + 125, 61, 45, 41, 40, 163, 36, 49, 123, 125, 10}; + +//------------------------------------------------------------------------------------ + +uint8 gamePaused=0; // James17jun97 +//uint32 pause_text_bloc_no=0; // James17jun97 +uint8 graphics_level_fudged=0; // James10july97 +uint8 stepOneCycle=0; // for use while game paused + +//------------------------------------------------------------------------------------ +void PauseGame(void); // James17jun97 +void UnpauseGame(void); // James17jun97 +//------------------------------------------------------------------------------------ + +static const VersionSettings bs2_settings[] = { + /* Broken Sword 2 */ + {"bs2", "Broken Sword II", GID_BS2_FIRST, 99, VersionSettings::ADLIB_DONT_CARE, GF_DEFAULT_TO_1X_SCALER, "players.clu" }, + {NULL, NULL, 0, 0, VersionSettings::ADLIB_DONT_CARE, 0, NULL} +}; + +BS2State *g_bs2 = NULL; + +const VersionSettings *Engine_BS2_targetList() { + return bs2_settings; +} + +Engine *Engine_BS2_create(GameDetector *detector, OSystem *syst) { + return new BS2State(detector, syst); +} + +BS2State::BS2State(GameDetector *detector, OSystem *syst) + : Engine(detector, syst) { + + _detector = detector; + _syst = syst; + g_bs2 = this; +} + + +void BS2State::errorString(const char *buf1, char *buf2) { + strcpy(buf2, buf1); +} + +int32 InitialiseGame(void) +{ +//init engine drivers + + uint8 *file; + + Zdebug("CALLING: Init_memory_manager"); + Init_memory_manager(); // get some falling RAM and put it in your pocket, never let it slip away + Zdebug("RETURNED."); + + Zdebug("CALLING: res_man.InitResMan"); + res_man.InitResMan(); // initialise the resource manager + Zdebug("RETURNED from res_man.InitResMan"); + + // initialise global script variables + file=res_man.Res_open(1); // res 1 is the globals list + Zdebug("CALLING: SetGlobalInterpreterVariables"); + SetGlobalInterpreterVariables((int32*)(file+sizeof(_standardHeader))); + Zdebug("RETURNED."); +// res_man.Res_close(1); // DON'T CLOSE VARIABLES RESOURCE - KEEP IT OPEN AT VERY START OF MEMORY SO IT CAN'T MOVE! + + file=res_man.Res_open(8); // DON'T CLOSE PLAYER OBJECT RESOURCE - KEEP IT OPEN IN MEMORY SO IT CAN'T MOVE! + + //---------------------------------------- + Zdebug("CALLING: InitialiseFontResourceFlags"); + InitialiseFontResourceFlags(); // Set up font resource variables for this language version (James31july97) + // Also set the windows application name to the proper game name + Zdebug("RETURNED."); + //---------------------------------------- + + Zdebug("CALLING: Init_console"); + Init_console(); // set up the console system + Zdebug("RETURNED."); + + #ifdef _DEBUG + Zdebug("CALLING: Init_start_menu"); + Init_start_menu(); // read in all the startup information + Zdebug("RETURNED from Init_start_menu"); + #endif // _DEBUG + + + + Zdebug("CALLING: Init_text_bloc_system"); + Init_text_bloc_system(); // no blocs live + Zdebug("RETURNED."); + + Zdebug("CALLING: Init_sync_system"); + Init_sync_system(); + Zdebug("RETURNED."); + + Zdebug("CALLING: Init_event_system"); + Init_event_system(); + Zdebug("RETURNED."); + + Zdebug("CALLING: Init_fx_queue"); + Init_fx_queue(); // initialise the sound fx queue + Zdebug("RETURNED."); + +#ifdef _DEMO // demo only + DEMO=1; // set script variable +#endif + + return(0); +} +//------------------------------------------------------------------------------------ +void Close_game() //Tony11Oct96 +{ + Zdebug("Close_game() STARTING:"); +//avoid corruption when windows kicks back in + EraseBackBuffer(); + FlipScreens(); + EraseBackBuffer(); + FlipScreens(); + + Kill_music(); // Stop music instantly! (James22aug97) + Close_memory_manager(); // free the memory again + res_man.Close_ResMan(); + + Zdebug("Close_game() DONE."); +} +//------------------------------------------------------------------------------------ +int32 GameCycle(void) +{ +//do one game cycle + + + { + if (LLogic.Return_run_list()) //got a screen to run? + { + do //run the logic session UNTIL a full loop has been performed + { + Reset_render_lists(); // reset the graphic 'buildit' list before a new logic list (see FN_register_frame) + Reset_mouse_list(); // reset the mouse hot-spot list (see FN_register_mouse & FN_register_frame) + } + while(LLogic.Process_session()); //keep going as long as new lists keep getting put in - i.e. screen changes + } + else //start the console and print the start options perhaps? + { + StartConsole(); + Print_to_console("AWAITING START COMMAND: (Enter 's 1' then 'q' to start from beginning)"); + } + } + + if (this_screen.scroll_flag) // if this screen is wide + Set_scrolling(); // recompute the scroll offsets every game-cycle + + Mouse_engine(); //check the mouse + + Process_fx_queue(); + + res_man.Res_next_cycle(); // update age and calculate previous cycle memory usage + + if (quitGame) + return(1); + else + return(0); +} +//------------------------------------------------------------------------------------ +// int main(int argc, char *argv[]) +// int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +void BS2State::go() +{ + uint32 rv; + uint8 breakOut = 0; + char c; + int j=100; + uint32 pc=0; + +// Zdebug("[%s]", lpCmdLine); + + + #ifndef _DEBUG + DisableQuitKey(); // so cannot use Ctrl-Q from the release versions (full game or demo) + #endif + + + if (RunningFromCd()) //stop this game being run from CD + exit(-1); + + + + // Call the application "Revolution" until the resource manager is ready to dig the name out of a text file + // See InitialiseGame() which calls InitialiseFontResourceFlags() in maketext.cpp + // Have to do it like this since we cannot really fire up the resource manager until a window + // has been created as any errors are displayed via a window, thus time becomes a loop. + + Zdebug("CALLING: InitialiseWindow"); + // rv = InitialiseWindow(hInstance, hPrevInstance, lpCmdLine, nCmdShow, "Revolution"); + rv = RD_OK; + Zdebug("RETURNED with rv = %.8x", rv); + if (rv != RD_OK) + { + // ReportDriverError(rv); + return; + } + + Zdebug("CALLING: InitialiseDisplay"); + // rv = InitialiseDisplay(640, 480, 8, RD_FULLSCREEN); + _syst->init_size(640, 480); + rv = RD_OK; + + Zdebug("RETURNED with rv = %.8x", rv); + if (rv != RD_OK) + { + // ReportDriverError(rv); + CloseAppWindow(); + return; + } + + Zdebug("CALLING: ReadOptionSettings"); + ReadOptionSettings(); //restore the menu settings + Zdebug("RETURNED."); + + Zdebug("CALLING: InitialiseSound"); + rv = InitialiseSound(22050, 2, 16); + Zdebug("RETURNED with rv = %.8x", rv); + // don't care if this fails, because it should still work without sound cards + // but it should set a global system flag so that we can avoid loading sound fx & streaming music + // because they won't be heard anyway +/* + if (rv != RD_OK) + { + ReportDriverError(rv); + CloseAppWindow(); + return(0); + } +*/ + + Zdebug("CALLING: InitialiseGame"); + if (InitialiseGame()) + { + Zdebug("RETURNED from InitialiseGame - closing game"); + RestoreDisplay(); + CloseAppWindow(); + return; + } + Zdebug("RETURNED from InitialiseGame - ok"); + + + +//check for restore game on startup - at the mo any passed argument is good enough to trigger this + // if (lpCmdLine[0]) //non zero + if (0) + { + Set_mouse(NORMAL_MOUSE_ID); + + if (!Restore_control()) // restore a game + Start_game(); + } + //------------------------------------------------------------- + // release versions only (full-game and demo) +// #if NDEBUG // comment this out for debug versions to start game automatically! + else + Start_game(); +// #endif // comment this out for debug versions to start game automatically! + //------------------------------------------------------------- + + + Zdebug("CALLING: InitialiseRenderCycle"); + InitialiseRenderCycle(); + Zdebug("RETURNED."); + + while (TRUE) + { + if (ServiceWindows() == RDERR_APPCLOSED) + { + break; // break out of main game loop + } + + + // check for events + parseEvents(); +#ifdef _DEBUG + if (grabbingSequences && (!console_status)) + GrabScreenShot(); +#endif + + while (!gotTheFocus) + { + if (ServiceWindows() == RDERR_APPCLOSED) + { + breakOut = 1; + break; // break out of this while-loop + } + } + + if (breakOut) // if we are closing down the game + break; // break out of main game loop + +//----- + +#ifdef _DEBUG + if (console_status) + { + if (One_console()) + { + EndConsole(); + UnpauseAllSound(); // see sound.cpp + } + } +#endif + + if (!console_status) //not in console mode - if the console is quit we want to get a logic cycle in before + { //the screen is build. Mostly because of first scroll cycle stuff +#ifdef _DEBUG + if (stepOneCycle) // if we've just stepped forward one cycle while the game was paused + { + PauseGame(); + stepOneCycle=0; + } +#endif + if (KeyWaiting()) + { + ReadKey(&c); +#ifdef _DEBUG + if (c==27) // ESC whether paused or not + { + PauseAllSound(); // see sound.cpp + StartConsole(); // start the console + } + else +#endif + if (gamePaused) // if currently paused + { + if (toupper(c)=='P') // 'P' while paused = unpause! + { + UnpauseGame(); + } +#ifdef _DEBUG + else if (toupper(c)==' ') // SPACE bar while paused = step one frame! + { + stepOneCycle=1; // step through one game cycle + UnpauseGame(); + } +#endif // _DEBUG + } + else if (toupper(c)=='P') // 'P' while not paused = pause! + { + PauseGame(); + } +#ifdef _DEBUG // frame-skipping only allowed on debug version + else if (toupper(c)=='S') // 'S' toggles speed up (by skipping display rendering) + { + renderSkip = 1 - renderSkip; + } +#endif // _DEBUG + } + + if (gamePaused==0) // skip GameCycle if we're paused + { +#ifdef _DEBUG + gameCycle += 1; +#endif + + if (GameCycle()) + break; // break out of main game loop + } + +#ifdef _DEBUG + Build_debug_text(); // creates the debug text blocks +#endif // _DEBUG + } +//----- + + // James (24mar97) + +#ifdef _DEBUG + if ((console_status)||(renderSkip==0)||(gameCycle%4 == 0)) // if not in console & 'renderSkip' is set, only render display once every 4 game-cycles + Build_display(); // create and flip the screen +#else + Build_display(); // create and flip the screen +#endif // _DEBUG + + } + + Close_game(); //close engine systems down + RestoreDisplay(); + CloseAppWindow(); + + return; //quit the game +} +//------------------------------------------------------------------------------------ +int RunningFromCd() +{ + char sCDName[MAX_PATH]; + char sRoot[MAX_PATH]; + DWORD dwMaxCompLength, dwFSFlags; +/* + GetModuleFileName(NULL , sRoot, _MAX_PATH); + *(strchr(sRoot,'\\')+1) = '\0'; + + if (!GetVolumeInformation(sRoot, sCDName,_MAX_PATH, NULL, &dwMaxCompLength, &dwFSFlags, NULL, 0)) + return -1; + if (!scumm_strnicmp(sCDName,CD1_LABEL,6)) + return 1; + if (!scumm_strnicmp(sCDName,CD2_LABEL,6)) + return 2; +*/ + return 0; +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------------ +void Start_game(void) //Tony29May97 +{ +//boot the game straight into a start script + + Zdebug("Start_game() STARTING:"); + +#ifdef _DEMO + #define SCREEN_MANAGER_ID 19 // DOCKS SECTION START +#else + #define SCREEN_MANAGER_ID 949 // INTRO & PARIS START +#endif + + char *raw_script; + char *raw_data_ad; + uint32 null_pc=1; // the required start-scripts are both script #1 in the respective ScreenManager objects + + raw_data_ad = (char*) (res_man.Res_open(8)); // open george object, ready for start script to reference + raw_script = (char*) (res_man.Res_open(SCREEN_MANAGER_ID)); // open the ScreenManager object + + RunScript ( raw_script, raw_data_ad, &null_pc ); // run the start script now (because no console) + + res_man.Res_close(SCREEN_MANAGER_ID); // close the ScreenManager object + res_man.Res_close(8); // close george + + Zdebug("Start_game() DONE."); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ +void PauseGame(void) // James17jun97 +{ +// uint8 *text; + +// text = FetchTextLine( res_man.Res_open(3258), 449 ); // open text file & get the line "PAUSED" +// pause_text_bloc_no = Build_new_block(text+2, 320, 210, 640, 184, RDSPR_TRANS+RDSPR_DISPLAYALIGN, SPEECH_FONT_ID, POSITION_AT_CENTRE_OF_BASE); +// res_man.Res_close(3258); // now ok to close the text file + + //--------------------------- + // don't allow Pause while screen fading or while black (James 03sep97) + if(GetFadeStatus()!=RDFADE_NONE) + return; + //--------------------------- + + PauseAllSound(); + +//make a normal mouse + ClearPointerText(); +// mouse_mode=MOUSE_normal; + SetLuggageAnim(NULL, NULL); //this is the only place allowed to do it this way + Set_mouse(NULL); // blank cursor + mouse_touching=1; //forces engine to choose a cursor + + if (current_graphics_level==3) // if level at max + { + UpdateGraphicsLevel(3,2); // turn down because palette-matching won't work when dimmed + graphics_level_fudged=1; + } + + if (stepOneCycle==0) // don't dim it if we're single-stepping through frames + { + DimPalette(); // dim the palette during the pause (James26jun97) + } + + gamePaused=1; +} +//------------------------------------------------------------------------------------ +void UnpauseGame(void) // James17jun97 +{ +// Kill_text_bloc(pause_text_bloc_no); // removed "PAUSED" from screen + + if ((OBJECT_HELD)&&(real_luggage_item)) + Set_luggage(real_luggage_item); + + UnpauseAllSound(); + + SetFullPalette(0xffffffff); // put back game screen palette; see Build_display.cpp (James26jun97) + + if (graphics_level_fudged) // if level at max + { + UpdateGraphicsLevel(2,3); // turn up again + graphics_level_fudged=0; + } + + gamePaused=0; + unpause_zone=2; + + if ((!mouse_status)||(choosing)) //if mouse is about or we're in a chooser menu + Set_mouse(NORMAL_MOUSE_ID); +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + diff --git a/sword2/sword2.h b/sword2/sword2.h new file mode 100644 index 0000000000..0ec86e8c39 --- /dev/null +++ b/sword2/sword2.h @@ -0,0 +1,62 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _SWORD2 +#define _SWORD2 + +//#include "src\driver96.h" + + +#ifdef _PCF76 // Bodge for PCF76 version so that their demo CD can be labelled "PCF76" rather than "RBSII1" + #define CD1_LABEL "PCF76" +#else + #define CD1_LABEL "RBSII1" +#endif + + +#define CD2_LABEL "RBSII2" + +void Close_game(); //Tony11Oct96 + +void PauseGame(void); // James17jun97 +void UnpauseGame(void); // James17jun97 +void Start_game(void); // James13aug97 + +#define HEAD_LEN 8 + +extern uint8 version_string[]; // for displaying from the console +extern uint8 unencoded_name[]; + + +// TODO move stuff into class +class BS2State : public Engine { + void errorString(const char *buf_input, char *buf_output); + public: + BS2State(GameDetector *detector, OSystem *syst); + void go(); + void parseEvents(); + OSystem *_syst; + GameDetector *_detector; + private: + bool _quit; +}; + +extern BS2State *g_bs2; + +#endif diff --git a/sword2/sync.cpp b/sword2/sync.cpp new file mode 100644 index 0000000000..7f8454db52 --- /dev/null +++ b/sword2/sync.cpp @@ -0,0 +1,179 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//------------------------------------------------------------------------------------ +#include <stdio.h> + +//#include "src\driver96.h" +#include "console.h" +#include "debug.h" +#include "defs.h" +#include "interpreter.h" +#include "memory.h" +#include "object.h" +#include "sync.h" +//------------------------------------------------------------------------------------ +typedef struct +{ + uint32 id; + uint32 sync; +} _sync_unit; //haaaaaaaa + +#define MAX_syncs 10 //there wont be many will there. probably 2 at most i reckon + + +_sync_unit sync_list[MAX_syncs]; + +//------------------------------------------------------------------------------------ +void Init_sync_system(void) //Tony27Nov96 +{ +//set list to 0's + + uint32 j; + + + + for (j=0;j<MAX_syncs;j++) + sync_list[j].id=0; + + +} +//------------------------------------------------------------------------------------ +int32 FN_send_sync(int32 *params) //Tony27Nov96 +{ +//param 0 sync's recipient +//param 1 sync value + + + uint32 current_sync=0; + + + if (sync_list[current_sync].id) + { + do + current_sync++; + while(sync_list[current_sync].id); //zip along until we find a free slot + + } + +// Zdebug(" %d sending sync %d to %d", ID, params[1], params[0]); + + + sync_list[current_sync].id=params[0]; + sync_list[current_sync].sync=params[1]; + + + + return(IR_CONT); +} +//------------------------------------------------------------------------------------ +void Clear_syncs(uint32 id) //Tony27Nov96 +{ +//clear any syncs registered for this id +//call this just after the id has been processed + + uint32 j; + + +//there could in theory be more than one sync waiting for us so clear the lot + + for (j=0;j<MAX_syncs;j++) + if (sync_list[j].id==id) + { //Zdebug("removing sync %d for %d", j, id); + sync_list[j].id=0; + } + + +} +//------------------------------------------------------------------------------------ +uint32 Get_sync(void) //Tony27Nov96 +{ + // check for a sync waiting for this character + // - called from system code eg. from inside FN_anim(), to see if animation to be quit + + uint32 j; + + + for (j=0;j<MAX_syncs;j++) + if (sync_list[j].id == ID) + return(1); //means sync found Tony12July97 + +// return(sync_list[j].sync); //return sync value waiting + + + + return(0); //no sync found + +} +//------------------------------------------------------------------------------------ +int32 FN_get_sync(int32 *params) //Tony27Nov96 +{ +// check for a sync waiting for this character +// - called from script + +//params none + + + uint32 j; + + + for (j=0;j<MAX_syncs;j++) + if (sync_list[j].id == ID) + { RESULT=sync_list[j].sync; + return(IR_CONT); //return sync value waiting + } + + RESULT=0; + + if (params); + + return(IR_CONT); //no sync found + +} +//------------------------------------------------------------------------------------ +int32 FN_wait_sync(int32 *params) //Tony27Nov96 +{ +//keep calling until a sync recieved + +//params none + + + uint32 j; + + j=ID; + + +// Zdebug("%d waits", ID); + + + for (j=0;j<MAX_syncs;j++) + if (sync_list[j].id == ID) + { RESULT=sync_list[j].sync; + //Zdebug(" go"); + return(IR_CONT); //return sync value waiting + } + + if (params); + + return(IR_REPEAT); //back again next cycle + +} +//------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------ + diff --git a/sword2/sync.h b/sword2/sync.h new file mode 100644 index 0000000000..4db00c4731 --- /dev/null +++ b/sword2/sync.h @@ -0,0 +1,32 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _SYNC +#define _SYNC + +//#include "src\driver96.h" +#include "object.h" + + +void Init_sync_system(void); //Tony27Nov96 +void Clear_syncs(uint32 id); //Tony27Nov96 +uint32 Get_sync(void); //Tony27Nov96 + + +#endif diff --git a/sword2/tony_gsdk.cpp b/sword2/tony_gsdk.cpp new file mode 100644 index 0000000000..ea39086166 --- /dev/null +++ b/sword2/tony_gsdk.cpp @@ -0,0 +1,151 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//=================================================================================================================== +// +// File - tony_gsdk.cpp +// +//=================================================================================================================== + + +//general odds and ends + +#include <sys/type.h> +#include <sys/stat.h> +#include <fcntl.h> +//#include <io.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +//#include "src\driver96.h" +#include "debug.h" +#include "header.h" +#include "layers.h" +#include "memory.h" +#include "protocol.h" +#include "resman.h" +#include "tony_gsdk.h" + +// TODO replace with file class + +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +uint32 Read_file(char *name, mem **membloc, uint32 uid) //Tony25Apr96 +{ +//read the file in and place into an allocated MEM_float block +//used for non resources manager files - stuff like fonts, etc. +//returns bytes read or 0 for error + + FILE *fh=0; //file pointer + uint32 end; + + + + fh = fopen(name, "rb"); //open for binary reading + + if (fh==NULL) + { Zdebug("Read_file cannot open %s", name); + return(0); + } + +//ok, find the length and read the file in + fseek(fh, 0, SEEK_END); //get size of file + end = ftell(fh); //finally got the end + + *membloc= Twalloc(end, MEM_float, uid); //reserve enough floating memory for the file + + fseek( fh, 0, SEEK_SET ); //back to beginning of file + + if (fread( (*membloc)->ad, sizeof(char), end,fh)==-1) + { Zdebug("Read_file read fail %d", name); + return(0); + } + + fclose(fh); + + return(end); //ok, done it - return bytes read +} +//----------------------------------------------------------------------------------------------------------------------- +int32 Direct_read_file(char *name, char *ad) //Tony1May96 +{ +//load the file directly into the memory location passed +//memory must be pre-allocated + + FILE *fh=0; //file pointer + uint32 end; + + + fh = fopen(name, "rb"); //open for binary reading + + if (fh==NULL) + { Zdebug("Direct_read_file cannot open %s", name); + return(0); + } + +//ok, find the length and read the file in + fseek(fh, 0, SEEK_END); //get size of file + end = ftell(fh); //finally got the end + fseek( fh, 0, SEEK_SET ); //back to beginning of file + + if (fread( ad, sizeof(char), end,fh)==-1) + { Zdebug("Direct_read_file read fail %d", name); + return(0); + } + + fclose(fh); + + + return(end); //ok, done it - return bytes read +} +//----------------------------------------------------------------------------------------------------------------------- +int32 Direct_write_file(char *name, char *ad, uint32 total_bytes) //Tony1May96 +{ +//load the file directly into the memory location passed + int fh; + + //fh = open(name, _O_RDWR | _O_CREAT); //open for reading + fh = open(name, O_RDWR | O_CREAT); //open for reading + + if (fh==-1) + { Zdebug("Direct_write_file open fail %d", name); + return(-1); + } + + if (write( fh, ad, total_bytes)==-1) + { Zdebug("Direct_write_file write fail %d", name); + return(-1); + } + + close(fh); + + return(0); //ok, done it +} + +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------- + diff --git a/sword2/tony_gsdk.h b/sword2/tony_gsdk.h new file mode 100644 index 0000000000..b5b9cf3f65 --- /dev/null +++ b/sword2/tony_gsdk.h @@ -0,0 +1,33 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef TONY_GSDK +#define TONY_GSDK + +//#include "src\driver96.h" +#include "memory.h" + + + +uint32 Read_file(char *name, mem **membloc, uint32 uid); +int32 Direct_read_file(char *name, char *ad); +int32 Direct_write_file(char *name, char *ad, uint32 total_bytes); + + +#endif diff --git a/sword2/walker.cpp b/sword2/walker.cpp new file mode 100644 index 0000000000..9ae5c5e0fe --- /dev/null +++ b/sword2/walker.cpp @@ -0,0 +1,932 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +//-------------------------------------------------------------------------------------- +// WALKER.CPP by James (14nov96) + +// script functions for moving megas about the place & also for keeping tabs on them + +// FN_walk() // walk to (x,y,dir) +// FN_walk_to_anim() // walk to start position of anim +// FN_turn() // turn to (dir) +// FN_stand_at() // stand at (x,y,dir) +// FN_stand() // stand facing (dir) +// FN_stand_after_anim() // stand at end position of anim +// FN_face_id() // turn to face object (id) +// FN_face_xy() // turn to face point (x,y) +// FN_is_facing() // is mega (id) facing us? +// FN_get_pos() // get details of another mega's position + +//-------------------------------------------------------------------------------------- + +//#include "src\driver96.h" +#include "console.h" +#include "defs.h" +#include "events.h" +#include "function.h" +#include "interpreter.h" +#include "logic.h" // for FN_add_to_kill_list +#include "object.h" +#include "protocol.h" +#include "router.h" +#include "sync.h" + +//-------------------------------------------------------------------------------------- + +int16 standby_x; // see FN_set_standby_coords +int16 standby_y; +uint8 standby_dir; + +//-------------------------------------------------------------------------------------- +/* +uint8 Check_walk_anim_ok( Object_mega *ob_mega, Object_walkdata *ob_walkdata ); +//-------------------------------------------------------------------------------------- +// NEW CODE TO VERIFY THAT THE WALK-ANIM CONTAINS NO INVALID FRAMES! +// (James 15sep97) +uint8 Check_walk_anim_ok( Object_mega *ob_mega, Object_walkdata *ob_walkdata ) +{ + int32 walk_pc=0; + _walkData *walkAnim; + uint8 *anim_file; + _animHeader *anim_head; + uint32 lastValidFrameNo; + uint8 ok=1; + + + anim_file = res_man.Res_open(ob_mega->megaset_res); // open mega-set file + anim_head = FetchAnimHeader( anim_file ); // set up pointer to the animation header + lastValidFrameNo = anim_head->noAnimFrames-1; // get last valid frame number + res_man.Res_close(ob_mega->megaset_res); // close file + + walkAnim = LockRouteMem(); // lock the _walkData array + + while (ok && (walkAnim[walk_pc].frame != 512)) // '512' id end-marker + { + if (walkAnim[walk_pc].frame > lastValidFrameNo) // if frame exceeds the allowed range + ok=0; + + walk_pc++; + } + + FloatRouteMem(); // allow _walkData array to float about memory again + + return(ok); +} +*/ +//-------------------------------------------------------------------------------------- +// walk mega to (x,y,dir) + +int32 FN_walk(int32 *params) // James (14nov96) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to object's walkdata structure + // 4 target x-coord + // 5 target y-coord + // 6 target direction + + Object_logic *ob_logic; + Object_graphic *ob_graph; + Object_mega *ob_mega; + Object_walkdata *ob_walkdata; + int16 target_x; + int16 target_y; + uint8 target_dir; + int8 route; + int32 walk_pc; + _walkData *walkAnim; + uint8 colliding=0; // set to 1 when collision avoided + + //---------------------------------------------------------------------------------------- + // get the parameters + + ob_logic = (Object_logic *)params[0]; + ob_graph = (Object_graphic *)params[1]; + ob_mega = (Object_mega *)params[2]; + + target_x = params[4]; + target_y = params[5]; + target_dir = params[6]; + + //---------------------------------------------------------------------------------------- + // if this is the start of the walk, calculate route + + if (ob_logic->looping==0) + { + //--------------------------- + // If we're already there, don't even bother allocating memory and calling the router, + // just quit back & continue the script! + // This avoids an embarassing mega stand frame appearing for one cycle when we're already + // in position for an anim eg. repeatedly clicking on same object to repeat an anim + // - no mega frame will appear in between runs of the anim. + + if ((ob_mega->feet_x == target_x) && (ob_mega->feet_y == target_y) + && (ob_mega->current_dir == target_dir)) + { + RESULT = 0; // 0 means ok - finished walk + return(IR_CONT); // may as well continue the script + } + + //--------------------------- + if ((params[6] < 0) || (params[6] > 8)) // invalid direction (NB. '8' means end walk on ANY direction) + Con_fatal_error("Invalid direction (%d) in FN_walk (%s line %u)",params[6],__FILE__,__LINE__); + //--------------------------- + + ob_walkdata = (Object_walkdata *)params[3]; + + ob_mega->walk_pc=0; //always + + AllocateRouteMem(); // set up mem for _walkData in route_slots[] & set mega's 'route_slot_id' accordingly + route = RouteFinder(ob_mega, ob_walkdata, target_x, target_y, target_dir); + + /* + if (id == PLAYER) + { + nExtraBars = 0; + nExtraNodes = 0; + if ((route == 1) || (route == 2)) + { + megaOnGrid = 0; // if we have just checked a grid with the mega on the grid take the mega off + reRouteGeorge = 0; + } + } + */ + + if ((route == 1) || (route == 2)) // 1=created route 2=zero route but may need to turn + { + //------------------------------------------- + ob_logic->looping = 1; // so script FN_walk loop continues until end of walk-anim + // need to animate the route now, so don't set result or return yet! + + ob_mega->currently_walking=1; // started walk(James23jun97) + // (see FN_get_player_savedata() in save_rest.cpp + //------------------------------------------- + } + else // 0=can't make route to target + { + FreeRouteMem(); // free up the walkdata mem block + RESULT = 1; // 1 means error, no walk created + return(IR_CONT); // may as well continue the script + } + + // ok, walk is about to start, so set the mega's graphic resource + ob_graph->anim_resource = ob_mega->megaset_res; + } + //---------------------------------------------------------------------------------------- + // double clicked an exit so quit the walk when screen is black + + else if ((EXIT_FADING) && (GetFadeStatus()==RDFADE_BLACK)) + { +// ok, thats it - back to script and change screen + + ob_logic->looping=0; // so script loop stops + FreeRouteMem(); // free up the walkdata mem block + + EXIT_CLICK_ID=0; // must clear in-case on the new screen there's a walk instruction (which would get cut short) +// EXIT_FADING=0; // this will be reset when we change screens, so we can use it in script to check if a 2nd-click came along + + + ob_mega->currently_walking=0; // finished walk (James23jun97) + // (see FN_get_player_savedata() in save_rest.cpp + + ob_mega->colliding=0; + + RESULT = 0; // 0 means ok + return(IR_CONT); // continue the script so that RESULT can be checked! + } + //---------------------------------------------------------------------------------------- + // get pointer to walkanim & current frame position + + walkAnim = LockRouteMem(); // lock the _walkData array + walk_pc = ob_mega->walk_pc; + + //---------------------------------------------------------------------------------------- + // if stopping the walk early, overwrite the next step with a slow-out, then finish + + if (Check_event_waiting()) + { + if ((walkAnim[walk_pc].step == 0) && (walkAnim[walk_pc+1].step == 1)) // at the beginning of a step + { + ob_walkdata = (Object_walkdata *)params[3]; + EarlySlowOut(ob_mega,ob_walkdata); + } + } +/* + else if (CheckForCollision()) + { + if ((walkAnim[walk_pc].step == 0) && (walkAnim[walk_pc+1].step == 1)) // at the beginning of a step + { + ob_walkdata = (Object_walkdata *)params[3]; + EarlySlowOut(ob_mega,ob_walkdata); + + ob_mega->colliding=1; + } + } +*/ + //------------------------------------------------------------------ + // get new frame of walk + + ob_graph->anim_pc = walkAnim[walk_pc].frame; + ob_mega->current_dir = walkAnim[walk_pc].dir; + ob_mega->feet_x = walkAnim[walk_pc].x; + ob_mega->feet_y = walkAnim[walk_pc].y; + + //------------------------------------------------------------------ + // check if NEXT frame is in fact the end-marker of the walk sequence + // so we can return to script just as the final (stand) frame of the walk is set + // - so that if followed by an anim, the anim's first frame replaces the final stand-frame + // of the walk (see below) + + if (walkAnim[walk_pc+1].frame==512) // '512' is end-marker + { + ob_logic->looping=0; // so script loop stops + FreeRouteMem(); // free up the walkdata mem block + + ob_mega->currently_walking=0; // finished walk(James23jun97) + // (see FN_get_player_savedata() in save_rest.cpp +/* + if (ID==CUR_PLAYER_ID) + { + george_walking = 0; + + if (megaOnGrid == 2) + megaOnGrid = 0; + } +*/ + + if (Check_event_waiting()) // if George's walk has been interrupted to run a new action script for instance + { // or Nico's walk has been interrupted by player clicking on her to talk + ob_mega->colliding=0; // Don't care about collision now we've got an event + Start_event(); + RESULT = 1; // 1 means didn't finish walk + return(IR_TERMINATE); + } + else if (ob_mega->colliding) // If we almost collided with another mega, + { // then we want to re-route from scratch. + ob_mega->colliding=0; // reset the flag now we've acknowledged the collision + return(IR_REPEAT); // Stop the script, but repeat this call next cycle + } + else + { + RESULT = 0; // 0 means ok - finished walk + return(IR_CONT); // CONTINUE the script so that RESULT can be checked! + // Also, if an anim command follows the FN_walk command, + // the 1st frame of the anim (which is always a stand frame itself) + // can replace the final stand frame of the walk, to hide the + // slight difference between the shrinking on the mega frames + // and the pre-shrunk anim start-frame. + } + } + //---------------------------------------------------------------------------------------- + // increment the walkanim frame number, float the walkanim & come back next cycle + + ob_mega->walk_pc++; + + FloatRouteMem(); // allow _walkData array to float about memory again + return(IR_REPEAT); // stop the script, but repeat this call next cycle + + //------------------------------------------------------------------ +} +//-------------------------------------------------------------------------------------- +// walk mega to start position of anim + +int32 FN_walk_to_anim(int32 *params) // James (14nov96) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to object's walkdata structure + // 4 anim resource id + + Object_logic *ob_logic; + uint8 *anim_file; + _animHeader *anim_head; + int32 pars[7]; + + //---------------------------------------------------------------------------------------- + // if this is the start of the walk, read anim file to get start coords + + ob_logic = (Object_logic *)params[0]; + + if (ob_logic->looping==0) + { + anim_file = res_man.Res_open(params[4]); // open anim file + anim_head = FetchAnimHeader( anim_file ); // point to animation header + + pars[4] = anim_head->feetStartX; // target_x + pars[5] = anim_head->feetStartY; // target_y + pars[6] = anim_head->feetStartDir; // target_dir + + res_man.Res_close(params[4]); // close anim file + + //------------------------------------------ + if ((pars[4]==0)&&(pars[5]==0)) // if start coords not yet set in anim header + { + pars[4] = standby_x; // use the standby coords + pars[5] = standby_y; // (which should be set beforehand in the script) + pars[6] = standby_dir; + + Zdebug("WARNING: FN_walk_to_anim(%s) used standby coords", FetchObjectName(params[4])); + } + + if ((pars[6] < 0) || (pars[6] > 7)) // check for invalid direction + Con_fatal_error("Invalid direction (%d) in FN_walk_to_anim (%s line %u)", pars[6],__FILE__,__LINE__); + + //------------------------------------------ + } + //---------------------------------------------------------------------------------------- + // set up the rest of the parameters for FN_walk() + + pars[0] = params[0]; + pars[1] = params[1]; + pars[2] = params[2]; + pars[3] = params[3]; // walkdata - needed for EarlySlowOut if player clicks elsewhere during the walk + + //------------------------------------------------------------------------------------------------------- + + return FN_walk(pars); // call FN_walk() with target coords set to anim start position +} + +//-------------------------------------------------------------------------------------- +// turn mega to <direction> +// just needs to call FN_walk() with current feet coords, so router can produce anim of turn frames + +int32 FN_turn(int32 *params) // James (15nov96) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to object's walkdata structure + // 4 target direction + + Object_logic *ob_logic; + Object_mega *ob_mega; + int32 pars[7]; + + // if this is the start of the turn, get the mega's current feet coords + the required direction + + ob_logic = (Object_logic *)params[0]; + + if (ob_logic->looping==0) + { + //-------------------------------------------- + if ((params[4] < 0) || (params[4] > 7)) // invalid direction + Con_fatal_error("Invalid direction (%d) in FN_turn (%s line %u)",params[4],__FILE__,__LINE__); + //-------------------------------------------- + + ob_mega = (Object_mega *)params[2]; + + pars[4] = ob_mega->feet_x; + pars[5] = ob_mega->feet_y; + pars[6] = params[4]; // DIRECTION to turn to + } + + //---------------------------------------------------------------------------------------- + // set up the rest of the parameters for FN_walk() + + pars[0] = params[0]; + pars[1] = params[1]; + pars[2] = params[2]; + pars[3] = params[3]; + + //---------------------------------------------------------------------------------------- + + return FN_walk(pars); // call FN_walk() with target coords set to feet coords +} +//-------------------------------------------------------------------------------------- +// stand mega at (x,y,dir) +// sets up the graphic object, but also needs to set the new 'current_dir' in the mega object, so the router knows in future + +int32 FN_stand_at(int32 *params) // James +{ + // params: 0 pointer to object's graphic structure + // 1 pointer to object's mega structure + // 2 target x-coord + // 3 target y-coord + // 4 target direction + + Object_mega *ob_mega; + Object_graphic *ob_graph; + + //---------------------------------------------------------------------------------------- + // check for invalid direction + + if ((params[4] < 0) || (params[4] > 7)) // invalid direction + Con_fatal_error("Invalid direction (%d) in FN_stand_at (%s line %u)",params[4],__FILE__,__LINE__); + + //---------------------------------------------------------------------------------------- + // set up pointers to the graphic & mega structure + + ob_graph = (Object_graphic *)params[0]; + ob_mega = (Object_mega *)params[1]; + + //---------------------------------------------------------------------------------------- + // set up the stand frame & set the mega's new direction + + ob_graph->anim_resource = ob_mega->megaset_res; // mega-set animation file + ob_mega->feet_x = params[2]; // x + ob_mega->feet_y = params[3]; // y + ob_graph->anim_pc = params[4]+96; // dir + first stand frame (always frame 96) + ob_mega->current_dir = params[4]; // dir + + //---------------------------------------------------------------------------------------- + + return(IR_CONT); // continue the script +} + +//-------------------------------------------------------------------------------------- +// stand mega in <direction> at current feet coords +// just needs to call FN_stand_at() with current feet coords + +int32 FN_stand(int32 *params) // James (15nov96) +{ + // params: 0 pointer to object's graphic structure + // 1 pointer to object's mega structure + // 2 target direction + + Object_mega *ob_mega = (Object_mega *)params[1]; + int32 pars[5]; + + pars[0] = params[0]; + pars[1] = params[1]; + pars[2] = ob_mega->feet_x; + pars[3] = ob_mega->feet_y; + pars[4] = params[2]; // DIRECTION to stand in + + return FN_stand_at(pars); // call FN_stand_at() with target coords set to feet coords +} +//-------------------------------------------------------------------------------------- +// stand mega at end position of anim + +int32 FN_stand_after_anim(int32 *params) // James (14nov96) +{ + // params: 0 pointer to object's graphic structure + // 1 pointer to object's mega structure + // 2 anim resource id + + uint8 *anim_file; + _animHeader *anim_head; + int32 pars[5]; + + //---------------------------------------------------------------------------------------- + // open the anim file & set up a pointer to the animation header + + anim_file = res_man.Res_open(params[2]); // open anim file + anim_head = FetchAnimHeader( anim_file ); + + //---------------------------------------------------------------------------------------- + // set up the parameter list for FN_walk_to() + + pars[0] = params[0]; + pars[1] = params[1]; + + pars[2] = anim_head->feetEndX; // x + pars[3] = anim_head->feetEndY; // y + pars[4] = anim_head->feetEndDir; // dir + + //---------------------------------------------------------------------------------------- + + if ((pars[2]==0)&&(pars[3]==0)) // if start coords not available either + { + pars[2] = standby_x; // use the standby coords + pars[3] = standby_y; // (which should be set beforehand in the script) + pars[4] = standby_dir; + + Zdebug("WARNING: FN_stand_after_anim(%s) used standby coords", FetchObjectName(params[2])); + } + + if ((pars[4] < 0) || (pars[4] > 7)) // check for invalid direction + Con_fatal_error("Invalid direction (%d) in FN_stand_after_anim (%s line %u)", pars[4],__FILE__,__LINE__); + + //---------------------------------------------------------------------------------------- + // close the anim file + + res_man.Res_close(params[2]); // close anim file + + //---------------------------------------------------------------------------------------- + + return FN_stand_at(pars); // call FN_stand_at() with target coords set to anim end position +} + +//-------------------------------------------------------------------------------------- +// stand mega at start position of anim + +int32 FN_stand_at_anim(int32 *params) // James (07feb97) +{ + // params: 0 pointer to object's graphic structure + // 1 pointer to object's mega structure + // 2 anim resource id + + uint8 *anim_file; + _animHeader *anim_head; + int32 pars[5]; + + //---------------------------------------------------------------------------------------- + // open the anim file & set up a pointer to the animation header + + anim_file = res_man.Res_open(params[2]); // open anim file + anim_head = FetchAnimHeader( anim_file ); + + //---------------------------------------------------------------------------------------- + // set up the parameter list for FN_walk_to() + + pars[0] = params[0]; + pars[1] = params[1]; + + pars[2] = anim_head->feetStartX; // x + pars[3] = anim_head->feetStartY; // y + pars[4] = anim_head->feetStartDir; // dir + + if ((pars[2]==0)&&(pars[3]==0)) // if start coords not available + { + pars[2] = standby_x; // use the standby coords + pars[3] = standby_y; // (which should be set beforehand in the script) + pars[4] = standby_dir; + + Zdebug("WARNING: FN_stand_at_anim(%s) used standby coords", FetchObjectName(params[2])); + } + + if ((pars[4] < 0) || (pars[4] > 7)) // check for invalid direction + Con_fatal_error("Invalid direction (%d) in FN_stand_after_anim (%s line %u)", pars[4],__FILE__,__LINE__); + + //------------------------------------------------------------------------------------------------------- + // close the anim file + + res_man.Res_close(params[2]); // close anim file + + //------------------------------------------------------------------------------------------------------- + + return FN_stand_at(pars); // call FN_stand_at() with target coords set to anim end position +} + +//-------------------------------------------------------------------------------------- +// Code to workout direction from start to dest + +#define diagonalx 36 // used in what_target not valid for all megas jps 17mar95 +#define diagonaly 8 + + +int What_target(int startX, int startY, int destX, int destY) //S2.1(20Jul95JPS) +{ + int tar_dir; +//setting up + int deltaX = destX-startX; + int deltaY = destY-startY; + int signX = (deltaX > 0); + int signY = (deltaY > 0); + int slope; + + if ( (abs(deltaY) * diagonalx ) < (abs(deltaX) * diagonaly / 2)) + { + slope = 0;// its flat + } + else if ( (abs(deltaY) * diagonalx / 2) > (abs(deltaX) * diagonaly ) ) + { + slope = 2;// its vertical + } + else + { + slope = 1;// its diagonal + } + + if (slope == 0) //flat + { + if (signX == 1) // going right + { + tar_dir = 2; + } + else + { + tar_dir = 6; + } + } + else if (slope == 2) //vertical + { + if (signY == 1) // going down + { + tar_dir = 4; + } + else + { + tar_dir = 0; + } + } + else if (signX == 1) //right diagonal + { + if (signY == 1) // going down + { + tar_dir = 3; + } + else + { + tar_dir = 1; + } + } + else //left diagonal + { + if (signY == 1) // going down + { + tar_dir = 5; + } + else + { + tar_dir = 7; + } + } + return tar_dir; +} + +//-------------------------------------------------------------------------------------- +// turn mega to face point (x,y) on the floor +// just needs to call FN_walk() with current feet coords & direction computed by What_target() + +int32 FN_face_xy(int32 *params) // James (29nov96) +{ + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to object's walkdata structure + // 4 target x-coord + // 5 target y-coord + + Object_logic *ob_logic; + Object_mega *ob_mega; + int32 pars[7]; + + //---------------------------------------------------------------------------------------- + // if this is the start of the turn, get the mega's current feet coords + the required direction + + ob_logic = (Object_logic *)params[0]; + + if (ob_logic->looping==0) + { + ob_mega = (Object_mega *)params[2]; + + pars[4] = ob_mega->feet_x; + pars[5] = ob_mega->feet_y; + pars[6] = What_target( ob_mega->feet_x, ob_mega->feet_y, params[4], params[5] ); // set target direction + } + + //---------------------------------------------------------------------------------------- + // set up the rest of the parameters for FN_walk() + + pars[0] = params[0]; + pars[1] = params[1]; + pars[2] = params[2]; + pars[3] = params[3]; + + //---------------------------------------------------------------------------------------- + + return FN_walk(pars); // call FN_walk() with target coords set to feet coords +} +//-------------------------------------------------------------------------------------- +int32 FN_face_mega(int32 *params) //S2.1(3mar95jps) Tony29Nov96 +{ +//params 0 pointer to object's logic structure +// 1 pointer to object's graphic structure +// 2 pointer to object's mega structure +// 3 pointer to object's walkdata structure + +// 4 id of target mega to face + + uint32 null_pc=3; //get ob_mega + char *raw_script_ad; + int32 pars[7]; + Object_logic *ob_logic; + Object_mega *ob_mega; + _standardHeader *head; + + + ob_mega = (Object_mega *)params[2]; + ob_logic = (Object_logic *)params[0]; + + + if (ob_logic->looping==0) + { + +// get targets info + head = (_standardHeader*) res_man.Res_open(params[4]); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("FN_face_mega %d not an object", params[4]); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + res_man.Res_close(params[4]); + +// engine_mega is now the Object_mega of mega we want to turn to face + + pars[3] = params[3]; + pars[4] = ob_mega->feet_x; + pars[5] = ob_mega->feet_y; + pars[6] = What_target( ob_mega->feet_x, ob_mega->feet_y, engine_mega.feet_x, engine_mega.feet_y ); + } + + + + pars[0] = params[0]; + pars[1] = params[1]; + pars[2] = params[2]; + pars[3] = params[3]; + + return FN_walk(pars); // call FN_walk() with target coords set to feet coords + +} +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +// FN_walk (here for reference instead of splitting a window) + + // params: 0 pointer to object's logic structure + // 1 pointer to object's graphic structure + // 2 pointer to object's mega structure + // 3 pointer to object's walkdata structure + // 4 target x-coord + // 5 target y-coord + // 6 target direction +//------------------------------------------------------------------------------------ +int32 FN_walk_to_talk_to_mega(int32 *params) //Tony2Dec96 +{ +//we route to left or right hand side of target id if possible +//target is a shrinking mega + + Object_mega *ob_mega; + Object_logic *ob_logic; + + uint32 null_pc=3; //4th script - get mega + char *raw_script_ad; + int32 pars[7]; + int scale; + int mega_seperation=params[5]; + _standardHeader *head; + + +//params 0 pointer to object's logic structure +// 1 pointer to object's graphic structure +// 2 pointer to object's mega structure +// 3 pointer to object's walkdata structure + +// 4 id of target mega to face +// 5 distance + + + ob_logic = (Object_logic*) params[0]; + ob_mega = (Object_mega*) params[2]; + + pars[0] = params[0]; // standard stuff + pars[1] = params[1]; + pars[2] = params[2]; + pars[3] = params[3]; // walkdata + + + + if (!ob_logic->looping) //not been here before so decide where to walk-to + { +// first request the targets info + head = (_standardHeader*) res_man.Res_open(params[4]); + if (head->fileType!=GAME_OBJECT) + Con_fatal_error("FN_walk_to_talk_to_mega %d not an object", params[4]); + + raw_script_ad = (char *)head; // (head+1) + sizeof(_object_hub); //get to raw script data + RunScript( raw_script_ad, raw_script_ad, &null_pc ); //call the base script - this is the graphic/mouse service call + res_man.Res_close(params[4]); + +// engine_mega is now the Object_mega of mega we want to route to + + + pars[5] = engine_mega.feet_y; // stand exactly beside the mega, ie. at same y-coord + + +// apply scale factor to walk distance + scale = (ob_mega->scale_a * ob_mega->feet_y + ob_mega->scale_b)/256; // Ay+B gives 256*scale ie. 256*256*true_scale for even better accuracy, ie. scale = (Ay+B)/256 + + mega_seperation= (mega_seperation*scale)/256; + +// Zdebug("seperation %d", mega_seperation); +// Zdebug(" target x %d, y %d", engine_mega.feet_x, engine_mega.feet_y); + + if (engine_mega.feet_x < ob_mega->feet_x) // target is left of us + { + pars[4] = engine_mega.feet_x+mega_seperation; // so aim to stand to their right + pars[6] = 5; // face down_left + } + else // ok, must be right of us + { + pars[4] = engine_mega.feet_x-mega_seperation; // so aim to stand to their left + pars[6] = 3; // face down_right + } + } + + //first cycle builds the route - thereafter merely follows it + + return FN_walk(pars); //call FN_walk() with target coords set to feet coords + //RESULT will be 1 when it finishes or 0 if it failed to build route +} +//------------------------------------------------------------------------------------ +int32 FN_set_walkgrid(int32 *params) // (6dec96 JEL) +{ + Con_fatal_error("FN_set_walkgrid no longer valid"); + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +// add this walkgrid resource to the list of those used for routing in this location +// - note this is ignored in the resource is already in the list + +int32 FN_add_walkgrid(int32 *params) // (03mar97 JEL) +{ + // params 0 id of walkgrid resource + + // all objects that add walkgrids must be restarted whenever we reneter a location + + if (ID != 8) // DON'T EVER KILL GEORGE! + FN_add_to_kill_list(params);// need to call this in case it wasn't called in script! ('params' just used as dummy param) + + AddWalkGrid(params[0]); + + res_man.Res_open(params[0]); // Touch the grid, getting it into memory. + res_man.Res_close(params[0]); + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +// remove this walkgrid resource from the list of those used for routing in this location +// - note that this is ignored if the resource isn't actually in the list + +int32 FN_remove_walkgrid(int32 *params) // (03mar97 JEL) +{ + // params 0 id of walkgrid resource + + RemoveWalkGrid(params[0]); + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_register_walkgrid(int32 *params) +{ + Con_fatal_error("FN_register_walkgrid no longer valid"); + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_set_scaling(int32 *params) // (6dec96 JEL) +{ + // params 0 pointer to object's mega structure + // 1 scale constant A + // 2 scale constant B + + // 256*s = A*y + B + + // where s is system scale, which itself is (256 * actual_scale) ie. s==128 is half size + + Object_mega *ob_mega = (Object_mega *) params[0]; + + ob_mega->scale_a = params[1]; + ob_mega->scale_b = params[2]; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- +int32 FN_set_standby_coords(int32 *params) // (10dec97 JEL) +{ + // set the standby walk coords to be used by FN_walk_to_anim & FN_stand_after_anim + // when the anim header's start/end coords are zero + + // useful during development; can stay in final game anyway + + // params 0 x-coord + // 1 y-coord + // 2 direction (0..7) + + //---------------------------------------------------------------------------------------- + // check for invalid direction + + if ((params[2] < 0) || (params[2] > 7)) // invalid direction + Con_fatal_error("Invalid direction (%d) in FN_set_standby_coords (%s line %u)",params[2],__FILE__,__LINE__); + + //---------------------------------------------------------------------------------------- + + standby_x = params[0]; + standby_y = params[1]; + standby_dir = params[2]; + + return(IR_CONT); // continue script +} +//--------------------------------------------------------------------------------------------------------------------- diff --git a/sword2/walker.h b/sword2/walker.h new file mode 100644 index 0000000000..864c239c60 --- /dev/null +++ b/sword2/walker.h @@ -0,0 +1,34 @@ +/* Copyright (C) 1994-2003 Revolution Software Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef _WALKER +#define _WALKER + +//#include "src\driver96.h" + + + +int32 FN_face_mega(int32 *params); +int32 FN_turn(int32 *params); +int32 FN_walk(int32 *params); // James (14nov96) +int32 FN_walk_to_anim(int32 *params); // James (14nov96) +int32 FN_stand_after_anim(int32 *params); // James (18jun97) +int32 FN_stand(int32 *params); // James + +#endif |