Updated GemRB (by Beholder)
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
# The application settings for Android libSDL port
|
||||
AppSettingVersion=16
|
||||
AppSettingVersion=17
|
||||
LibSdlVersion=1.2
|
||||
AppName="GemRB"
|
||||
AppFullName=net.sourceforge.gemrb
|
||||
ScreenOrientation=h
|
||||
InhibitSuspend=n
|
||||
AppDataDownloadUrl="GemRB data(local)|data1.zip"
|
||||
AppDataDownloadUrl="Baldur's gate 2 demo|http://sourceforge.net/projects/libsdl-android/files/gemrb/bg2demo.zip/download^!GemRB data(override)|override2.zip^!GemRB data(scripts)|scripts2.zip"
|
||||
SdlVideoResize=y
|
||||
SdlVideoResizeKeepAspect=y
|
||||
NeedDepthBuffer=n
|
||||
SwVideoMode=n
|
||||
AppUsesMouse=y
|
||||
AppNeedsTwoButtonMouse=y
|
||||
AppNeedsArrowKeys=n
|
||||
AppNeedsTextInput=y
|
||||
AppUsesJoystick=y
|
||||
AppUsesJoystick=n
|
||||
AppHandlesJoystickSensitivity=n
|
||||
AppUsesMultitouch=n
|
||||
NonBlockingSwapBuffers=n
|
||||
@@ -22,13 +23,12 @@ AppTouchscreenKeyboardKeysAmount=0
|
||||
AppTouchscreenKeyboardKeysAmountAutoFire=0
|
||||
RedefinedKeysScreenKb="LCTRL c p o e"
|
||||
MultiABI=y
|
||||
AppVersionCode=0631
|
||||
AppVersionName="0.6.3.1"
|
||||
AppVersionCode=0641
|
||||
AppVersionName="0.6.4.1"
|
||||
CompiledLibraries="sdl_mixer ogg vorbis openal png python"
|
||||
CustomBuildScript=n
|
||||
AppCflags='-fexceptions -finline-functions -O3 -DSTATIC_LINK=Yes -DHAVE_SNPRINTF -DTOUCHSCREEN'
|
||||
AppLdflags=''
|
||||
AppSubdirsBuild=''
|
||||
AppUseCrystaXToolchain=y
|
||||
AppCmdline='GemRB'
|
||||
ReadmeText='^You may press "Home" now - the data will be downloaded in background'
|
||||
|
||||
BIN
project/jni/application/gemrb/AndroidData/override2.zip
Normal file
BIN
project/jni/application/gemrb/AndroidData/override2.zip
Normal file
Binary file not shown.
BIN
project/jni/application/gemrb/AndroidData/scripts2.zip
Normal file
BIN
project/jni/application/gemrb/AndroidData/scripts2.zip
Normal file
Binary file not shown.
@@ -12,8 +12,14 @@ IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(gemrb gemrb_core)
|
||||
ELSE(WIN32)
|
||||
IF(APPLE)
|
||||
TARGET_LINK_LIBRARIES(gemrb gemrb_core ${SDL_LIBRARY}
|
||||
${SDL_MAIN_LIBRARY_PATH} ${COCOA_LIBRARY_PATH} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
if (STATIC_LINK)
|
||||
TARGET_LINK_LIBRARIES(gemrb ${SDL_LIBRARY}
|
||||
${SDL_MAIN_LIBRARY_PATH} ${COCOA_LIBRARY_PATH} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}
|
||||
gemrb_core ${plugins} -Wl,-all_load)
|
||||
else (STATIC_LINK)
|
||||
TARGET_LINK_LIBRARIES(gemrb gemrb_core ${SDL_LIBRARY}
|
||||
${SDL_MAIN_LIBRARY_PATH} ${COCOA_LIBRARY_PATH} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif (STATIC_LINK)
|
||||
ELSE(APPLE)
|
||||
if (STATIC_LINK)
|
||||
TARGET_LINK_LIBRARIES(gemrb ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
# Game Type [String] Use one of the following #
|
||||
# values: #
|
||||
# #
|
||||
# auto Attempt to autodetect game type #
|
||||
# #
|
||||
# bg1 Baldur's Gate #
|
||||
# bg2 Baldur's Gate 2 : SoA or ToB #
|
||||
# tob Baldur's Gate 2 : ToB (obsolete) #
|
||||
# iwd IceWind Dale #
|
||||
# iwd IceWind Dale (no How or ToTL installed)#
|
||||
# how IceWind Dale : HoW or ToTL #
|
||||
# iwd2 IceWind Dale 2 #
|
||||
# pst Planescape Torment #
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
# Game Type [String] Use one of the following #
|
||||
# values: #
|
||||
# #
|
||||
# auto Attempt to autodetect game type #
|
||||
# #
|
||||
# bg1 Baldur's Gate #
|
||||
# bg2 Baldur's Gate 2 : SoA or ToB #
|
||||
# tob Baldur's Gate 2 : ToB (obsolete) #
|
||||
# iwd IceWind Dale #
|
||||
# iwd IceWind Dale (no How or ToTL installed)#
|
||||
# how IceWind Dale : HoW or ToTL #
|
||||
# iwd2 IceWind Dale 2 #
|
||||
# pst Planescape Torment #
|
||||
|
||||
@@ -35,17 +35,17 @@
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <SDL/SDL.h>
|
||||
#include "audio.h"
|
||||
#include "Audio.h"
|
||||
|
||||
// pause audio playing if app goes in background
|
||||
static void appPutToBackground()
|
||||
{
|
||||
core->GetAudioDrv()->Pause();
|
||||
core->GetAudioDrv()->Pause();
|
||||
}
|
||||
// resume audio playing if app return to foreground
|
||||
static void appPutToForeground()
|
||||
{
|
||||
core->GetAudioDrv()->Resume();
|
||||
core->GetAudioDrv()->Resume();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -49,33 +49,33 @@ class GEM_EXPORT Audio : public Plugin {
|
||||
public:
|
||||
static const TypeID ID;
|
||||
public:
|
||||
Audio(void);
|
||||
virtual ~Audio();
|
||||
virtual bool Init(void) = 0;
|
||||
virtual Holder<SoundHandle> Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0) = 0;
|
||||
virtual Holder<SoundHandle> Play(const char* ResRef, unsigned int *length = 0) { return Play(ResRef, 0, 0, GEM_SND_RELATIVE, length); }
|
||||
virtual bool IsSpeaking() = 0;
|
||||
virtual AmbientMgr* GetAmbientMgr() { return ambim; }
|
||||
virtual void UpdateVolume(unsigned int flags = GEM_SND_VOL_MUSIC | GEM_SND_VOL_AMBIENTS) = 0;
|
||||
virtual bool CanPlay() = 0;
|
||||
virtual void ResetMusics() = 0;
|
||||
virtual bool Play() = 0;
|
||||
virtual bool Stop() = 0;
|
||||
virtual bool Pause() = 0;
|
||||
virtual bool Resume() = 0;
|
||||
virtual int CreateStream(Holder<SoundMgr>) = 0;
|
||||
virtual void UpdateListenerPos(int XPos, int YPos ) = 0;
|
||||
virtual void GetListenerPos(int &XPos, int &YPos ) = 0;
|
||||
virtual bool ReleaseStream(int stream, bool HardStop=false ) = 0;
|
||||
virtual int SetupNewStream( ieWord x, ieWord y, ieWord z,
|
||||
ieWord gain, bool point, bool Ambient) = 0;
|
||||
virtual int QueueAmbient(int stream, const char* sound) = 0;
|
||||
virtual void SetAmbientStreamVolume(int stream, int volume) = 0;
|
||||
virtual void QueueBuffer(int stream, unsigned short bits,
|
||||
int channels, short* memory, int size, int samplerate) = 0;
|
||||
Audio(void);
|
||||
virtual ~Audio();
|
||||
virtual bool Init(void) = 0;
|
||||
virtual Holder<SoundHandle> Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0) = 0;
|
||||
virtual Holder<SoundHandle> Play(const char* ResRef, unsigned int *length = 0) { return Play(ResRef, 0, 0, GEM_SND_RELATIVE, length); }
|
||||
virtual bool IsSpeaking() = 0;
|
||||
virtual AmbientMgr* GetAmbientMgr() { return ambim; }
|
||||
virtual void UpdateVolume(unsigned int flags = GEM_SND_VOL_MUSIC | GEM_SND_VOL_AMBIENTS) = 0;
|
||||
virtual bool CanPlay() = 0;
|
||||
virtual void ResetMusics() = 0;
|
||||
virtual bool Play() = 0;
|
||||
virtual bool Stop() = 0;
|
||||
virtual bool Pause() = 0;
|
||||
virtual bool Resume() = 0;
|
||||
virtual int CreateStream(Holder<SoundMgr>) = 0;
|
||||
virtual void UpdateListenerPos(int XPos, int YPos ) = 0;
|
||||
virtual void GetListenerPos(int &XPos, int &YPos ) = 0;
|
||||
virtual bool ReleaseStream(int stream, bool HardStop=false ) = 0;
|
||||
virtual int SetupNewStream( ieWord x, ieWord y, ieWord z,
|
||||
ieWord gain, bool point, bool Ambient) = 0;
|
||||
virtual int QueueAmbient(int stream, const char* sound) = 0;
|
||||
virtual void SetAmbientStreamVolume(int stream, int volume) = 0;
|
||||
virtual void QueueBuffer(int stream, unsigned short bits,
|
||||
int channels, short* memory, int size, int samplerate) = 0;
|
||||
|
||||
protected:
|
||||
AmbientMgr* ambim;
|
||||
AmbientMgr* ambim;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ FILE(GLOB gemrb_core_LIB_SRCS "*.cpp"
|
||||
GUI/Window.cpp
|
||||
GUI/WorldMapControl.cpp
|
||||
Scriptable/Actor.cpp
|
||||
Scriptable/ActorBlock.cpp
|
||||
Scriptable/Container.cpp
|
||||
Scriptable/Door.cpp
|
||||
Scriptable/InfoPoint.cpp
|
||||
Scriptable/Scriptable.cpp
|
||||
Scriptable/PCStatStruct.cpp
|
||||
System/CachedFileStream.cpp
|
||||
System/DataStream.cpp
|
||||
|
||||
@@ -273,6 +273,28 @@ void CharAnimations::SetColors(const ieDword *arg)
|
||||
SetupColors(PAL_HELMET);
|
||||
}
|
||||
|
||||
void CharAnimations::CheckColorMod()
|
||||
{
|
||||
if (!GlobalColorMod.locked) {
|
||||
if (GlobalColorMod.type != RGBModifier::NONE) {
|
||||
GlobalColorMod.type = RGBModifier::NONE;
|
||||
GlobalColorMod.speed = 0;
|
||||
change[0]=change[1]=change[2]=change[3]=true;
|
||||
}
|
||||
}
|
||||
unsigned int location;
|
||||
|
||||
for (location = 0; location < 32; ++location) {
|
||||
if (!ColorMods[location].phase) {
|
||||
if (ColorMods[location].type != RGBModifier::NONE) {
|
||||
ColorMods[location].type = RGBModifier::NONE;
|
||||
ColorMods[location].speed = 0;
|
||||
change[location>>3]=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharAnimations::SetupColors(PaletteType type)
|
||||
{
|
||||
Palette* pal = palette[(int)type];
|
||||
@@ -307,11 +329,16 @@ void CharAnimations::SetupColors(PaletteType type)
|
||||
if (GlobalColorMod.type != RGBModifier::NONE) {
|
||||
needmod = true;
|
||||
}
|
||||
//don't drop the palette, it disables rgb pulse effects
|
||||
//also don't bail out, we need to free the modified palette later
|
||||
//so this entire block is needless
|
||||
/*
|
||||
if ((colorcount == 0) && (needmod==false) ) {
|
||||
gamedata->FreePalette(palette[PAL_MAIN], PaletteResRef);
|
||||
gamedata->FreePalette(palette[PAL_MAIN], PaletteResRef);
|
||||
PaletteResRef[0]=0;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
for (int i = 0; i < colorcount; i++) {
|
||||
core->GetPalette( Colors[i]&255, size,
|
||||
&palette[PAL_MAIN]->col[dest] );
|
||||
@@ -529,6 +556,7 @@ CharAnimations::CharAnimations(unsigned int AnimID, ieDword ArmourLevel)
|
||||
Colors = NULL;
|
||||
int i,j;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
change[i] = true;
|
||||
modifiedPalette[i] = NULL;
|
||||
palette[i] = NULL;
|
||||
}
|
||||
@@ -2328,7 +2356,7 @@ void CharAnimations::PulseRGBModifiers()
|
||||
if (time - lastModUpdate > 400) lastModUpdate = time - 40;
|
||||
|
||||
int inc = (time - lastModUpdate)/40;
|
||||
bool change[4] = { false, false, false, false };
|
||||
|
||||
if (GlobalColorMod.type != RGBModifier::NONE &&
|
||||
GlobalColorMod.speed > 0)
|
||||
{
|
||||
@@ -2359,10 +2387,22 @@ void CharAnimations::PulseRGBModifiers()
|
||||
}
|
||||
}
|
||||
|
||||
if (change[0]) SetupColors(PAL_MAIN);
|
||||
if (change[1]) SetupColors(PAL_WEAPON);
|
||||
if (change[2]) SetupColors(PAL_OFFHAND);
|
||||
if (change[3]) SetupColors(PAL_HELMET);
|
||||
if (change[0]) {
|
||||
change[0]=0;
|
||||
SetupColors(PAL_MAIN);
|
||||
}
|
||||
if (change[1]) {
|
||||
change[1]=0;
|
||||
SetupColors(PAL_WEAPON);
|
||||
}
|
||||
if (change[2]) {
|
||||
change[2]=0;
|
||||
SetupColors(PAL_OFFHAND);
|
||||
}
|
||||
if (change[3]) {
|
||||
change[3]=0;
|
||||
SetupColors(PAL_HELMET);
|
||||
}
|
||||
|
||||
lastModUpdate += inc*40;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ public:
|
||||
unsigned long lastModUpdate;
|
||||
RGBModifier GlobalColorMod; // global color modification effect
|
||||
|
||||
bool change[4];
|
||||
Palette* palette[4];
|
||||
Palette* modifiedPalette[4];
|
||||
unsigned int AvatarsRowNum;
|
||||
@@ -157,8 +158,9 @@ public:
|
||||
void SetHelmetRef(const char* ref);
|
||||
void SetWeaponRef(const char* ref);
|
||||
void SetOffhandRef(const char* ref);
|
||||
void SetupColors(PaletteType type);
|
||||
void SetColors(const ieDword *Colors);
|
||||
void CheckColorMod();
|
||||
void SetupColors(PaletteType type);
|
||||
void LockPalette(const ieDword *Colors);
|
||||
|
||||
// returns an array of animations of size GetTotalPartCount()
|
||||
|
||||
@@ -76,12 +76,12 @@ void strnuprcpy(char* dest, const char *source, int count)
|
||||
*dest=0;
|
||||
}
|
||||
|
||||
// this one also filters spaces
|
||||
// this one also filters spaces, used to copy variables
|
||||
void strnspccpy(char* dest, const char *source, int count)
|
||||
{
|
||||
memset(dest,0,count);
|
||||
while(count--) {
|
||||
char c = pl_lowercase[(ieByte) *source];
|
||||
char c = pl_uppercase[(ieByte) *source];
|
||||
if (c!=' ') {
|
||||
*dest++=c;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#include "exports.h"
|
||||
#include "globals.h"
|
||||
|
||||
#include "GameScript/GameScript.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define IE_DLG_TR_TEXT 0x01
|
||||
@@ -37,6 +35,9 @@
|
||||
#define IE_DLG_SOLVED 0x100
|
||||
#define IE_DLG_QUEST_GROUP 0x4000 // this is a GemRB extension
|
||||
|
||||
class Condition;
|
||||
class Action;
|
||||
|
||||
struct DialogTransition {
|
||||
ieDword Flags;
|
||||
ieStrRef textStrRef;
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
#include "DisplayMessage.h"
|
||||
#include "Game.h"
|
||||
#include "GameData.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Video.h"
|
||||
#include "GameScript/GameScript.h"
|
||||
#include "GUI/GameControl.h"
|
||||
|
||||
//translate section values (journal, solved, unsolved, user)
|
||||
@@ -89,8 +91,13 @@ int DialogHandler::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgr
|
||||
}
|
||||
if (oldTarget) oldTarget->SetCircleSize();
|
||||
|
||||
GameControl *gc = core->GetGameControl();
|
||||
|
||||
if (!gc)
|
||||
return -1;
|
||||
|
||||
//check if we are already in dialog
|
||||
if (core->GetGameControl()->GetDialogueFlags()&DF_IN_DIALOG) {
|
||||
if (gc->GetDialogueFlags()&DF_IN_DIALOG) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -100,11 +107,12 @@ int DialogHandler::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgr
|
||||
}
|
||||
|
||||
//we need GUI for dialogs
|
||||
core->GetGameControl()->UnhideGUI();
|
||||
//but the guiscript must be in control here
|
||||
//gc->UnhideGUI();
|
||||
|
||||
//no exploring while in dialogue
|
||||
core->GetGameControl()->SetScreenFlags(SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_OR);
|
||||
core->GetGameControl()->SetDialogueFlags(DF_IN_DIALOG, BM_OR);
|
||||
gc->SetScreenFlags(/*SF_GUIENABLED|*/SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_OR);
|
||||
gc->SetDialogueFlags(DF_IN_DIALOG, BM_OR);
|
||||
|
||||
if (tgt->Type==ST_ACTOR) {
|
||||
Actor *tar = (Actor *) tgt;
|
||||
@@ -119,12 +127,13 @@ int DialogHandler::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgr
|
||||
video->MoveViewportTo( tgt->Pos.x-vp.w/2, tgt->Pos.y-vp.h/2 );
|
||||
//there are 3 bits, if they are all unset, the dialog freezes scripts
|
||||
if (!(dlg->Flags&7) ) {
|
||||
core->GetGameControl()->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR);
|
||||
gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR);
|
||||
}
|
||||
//opening control size to maximum, enabling dialog window
|
||||
core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND);
|
||||
core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR);
|
||||
core->SetEventFlag(EF_PORTRAIT);
|
||||
//but the guiscript must be in control here
|
||||
//core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND);
|
||||
//core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR);
|
||||
//core->SetEventFlag(EF_PORTRAIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -157,6 +166,8 @@ void DialogHandler::EndDialog(bool try_to_break)
|
||||
delete dlg;
|
||||
dlg = NULL;
|
||||
}
|
||||
// FIXME: it's not so nice having this here, but things call EndDialog directly :(
|
||||
core->GetGUIScriptEngine()->RunFunction( "GUIWORLD", "DialogEnded" );
|
||||
//restoring original size
|
||||
core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND);
|
||||
core->GetGameControl()->SetScreenFlags(SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_NAND);
|
||||
@@ -264,10 +275,15 @@ void DialogHandler::DialogChoose(unsigned int choose)
|
||||
if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this
|
||||
target->ClearActions();
|
||||
|
||||
// do not interrupt during dialog actions (needed for aerie.d polymorph block)
|
||||
char buf[20];
|
||||
strcpy(buf, "SetInterrupt(FALSE)");
|
||||
target->AddAction( GenerateAction( buf ) );
|
||||
for (unsigned int i = 0; i < tr->actions.size(); i++) {
|
||||
target->AddAction(tr->actions[i]);
|
||||
//GameScript::ExecuteAction( target, action );
|
||||
}
|
||||
strcpy(buf, "SetInterrupt(TRUE)");
|
||||
target->AddAction( GenerateAction( buf ) );
|
||||
}
|
||||
|
||||
int final_dialog = tr->Flags & IE_DLG_TR_FINAL;
|
||||
@@ -277,11 +293,6 @@ void DialogHandler::DialogChoose(unsigned int choose)
|
||||
EndDialog();
|
||||
}
|
||||
|
||||
// *** the commented-out line here should no longer be required, with instant handling ***
|
||||
// all dialog actions must be executed immediately
|
||||
//target->ProcessActions(true);
|
||||
// (do not clear actions - final actions can involve waiting/moving)
|
||||
|
||||
if (final_dialog) {
|
||||
return;
|
||||
}
|
||||
@@ -457,11 +468,11 @@ Scriptable *DialogHandler::GetTarget()
|
||||
if (actor) return actor;
|
||||
|
||||
Door *door = area->GetDoorByGlobalID(targetID);
|
||||
if (door) return door;
|
||||
if (door) return (Scriptable *)door;
|
||||
Container *container = area->GetContainerByGlobalID(targetID);
|
||||
if (container) return container;
|
||||
if (container) return (Scriptable *)container;
|
||||
InfoPoint *ip = area->GetInfoPointByGlobalID(targetID);
|
||||
if (ip) return ip;
|
||||
if (ip) return (Scriptable *)ip;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ unsigned int DisplayMessage::GetSpeakerColor(const char *&name, const Scriptable
|
||||
speaker_color = (ActorColor[4].r<<16) | (ActorColor[4].g<<8) | ActorColor[4].b;
|
||||
break;
|
||||
case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL:
|
||||
name = core->GetString( ((InfoPoint *) speaker)->DialogName );
|
||||
name = core->GetString( speaker->DialogName );
|
||||
speaker_color = 0xc0c0c0;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -35,10 +35,11 @@ static struct {
|
||||
const char* Name;
|
||||
EffectFunction Function;
|
||||
int Strref;
|
||||
int Flags;
|
||||
} Opcodes[MAX_EFFECTS];
|
||||
|
||||
static int initialized = 0;
|
||||
static EffectRef *effectnames = NULL;
|
||||
static EffectDesc *effectnames = NULL;
|
||||
static int effectnames_count = 0;
|
||||
static int pstflags = false;
|
||||
|
||||
@@ -54,6 +55,13 @@ bool EffectQueue::match_ids(Actor *target, int table, ieDword value)
|
||||
case 2: //EA
|
||||
stat = IE_EA; break;
|
||||
case 3: //GENERAL
|
||||
//this is a hack to support dead only projectiles in PST
|
||||
//if it interferes with something feel free to remove
|
||||
if (value==GEN_DEAD) {
|
||||
if (target->GetStat(IE_STATE_ID)&STATE_DEAD) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
stat = IE_GENERAL; break;
|
||||
case 4: //RACE
|
||||
stat = IE_RACE; break;
|
||||
@@ -165,49 +173,25 @@ int find_effect(const void *a, const void *b)
|
||||
return stricmp((const char *) a,((const EffectRef *) b)->Name);
|
||||
}
|
||||
|
||||
static EffectRef* FindEffect(const char* effectname)
|
||||
static EffectDesc* FindEffect(const char* effectname)
|
||||
{
|
||||
if( !effectname || !effectnames) {
|
||||
return NULL;
|
||||
}
|
||||
void *tmp = bsearch(effectname, effectnames, effectnames_count, sizeof(EffectRef), find_effect);
|
||||
void *tmp = bsearch(effectname, effectnames, effectnames_count, sizeof(EffectDesc), find_effect);
|
||||
if( !tmp) {
|
||||
printMessage( "EffectQueue", "", YELLOW);
|
||||
printf("Couldn't assign effect: %s\n", effectname );
|
||||
}
|
||||
return (EffectRef *) tmp;
|
||||
return (EffectDesc *) tmp;
|
||||
}
|
||||
|
||||
static EffectRef fx_protection_from_display_string_ref={"Protection:String",NULL,-1};
|
||||
|
||||
//special effects without level check (but with damage dices precalculated)
|
||||
static EffectRef diced_effects[] = {
|
||||
//core effects
|
||||
{"Damage",NULL,-1},
|
||||
{"CurrentHPModifier",NULL,-1},
|
||||
{"MaximumHPModifier",NULL,-1},
|
||||
//iwd effects
|
||||
{"BurningBlood",NULL,-1}, //iwd
|
||||
{"ColdDamage",NULL,-1},
|
||||
{"CrushingDamage",NULL,-1},
|
||||
{"VampiricTouch",NULL,-1},
|
||||
{"VitriolicSphere",NULL,-1},
|
||||
//pst effects
|
||||
{"TransferHP",NULL,-1},
|
||||
{NULL,NULL,0} };
|
||||
|
||||
//special effects without level check (but with damage dices not precalculated)
|
||||
static EffectRef diced_effects2[] = {
|
||||
{"BurningBlood2",NULL,-1}, //how/iwd2
|
||||
{"StaticCharge",NULL,-1}, //how/iwd2
|
||||
{"SoulEater",NULL,-1}, //how/iwd2
|
||||
{"LichTouch",NULL,-1}, //how
|
||||
{NULL,NULL,0} };
|
||||
static EffectRef fx_protection_from_display_string_ref = { "Protection:String", -1 };
|
||||
|
||||
inline static void ResolveEffectRef(EffectRef &effect_reference)
|
||||
{
|
||||
if( effect_reference.opcode==-1) {
|
||||
EffectRef* ref = FindEffect(effect_reference.Name);
|
||||
EffectDesc* ref = FindEffect(effect_reference.Name);
|
||||
if( ref && ref->opcode>=0) {
|
||||
effect_reference.opcode = ref->opcode;
|
||||
return;
|
||||
@@ -258,10 +242,11 @@ bool Init_EffectQueue()
|
||||
}
|
||||
}
|
||||
|
||||
EffectRef* poi = FindEffect( effectname );
|
||||
EffectDesc* poi = FindEffect( effectname );
|
||||
if( poi != NULL) {
|
||||
Opcodes[i].Function = poi->Function;
|
||||
Opcodes[i].Name = poi->Name;
|
||||
Opcodes[i].Flags = poi->Flags;
|
||||
//reverse linking opcode number
|
||||
//using this unused field
|
||||
if( (poi->opcode!=-1) && effectname[0]!='*') {
|
||||
@@ -274,14 +259,6 @@ bool Init_EffectQueue()
|
||||
}
|
||||
core->DelSymbol( eT );
|
||||
|
||||
//additional initialisations
|
||||
for (i=0;diced_effects[i].Name;i++) {
|
||||
ResolveEffectRef(diced_effects[i]);
|
||||
}
|
||||
for (i=0;diced_effects2[i].Name;i++) {
|
||||
ResolveEffectRef(diced_effects2[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -294,21 +271,21 @@ void EffectQueue_ReleaseMemory()
|
||||
effectnames = NULL;
|
||||
}
|
||||
|
||||
void EffectQueue_RegisterOpcodes(int count, const EffectRef* opcodes)
|
||||
void EffectQueue_RegisterOpcodes(int count, const EffectDesc* opcodes)
|
||||
{
|
||||
if( ! effectnames) {
|
||||
effectnames = (EffectRef*) malloc( (count+1) * sizeof( EffectRef ) );
|
||||
effectnames = (EffectDesc*) malloc( (count+1) * sizeof( EffectDesc ) );
|
||||
} else {
|
||||
effectnames = (EffectRef*) realloc( effectnames, (effectnames_count + count + 1) * sizeof( EffectRef ) );
|
||||
effectnames = (EffectDesc*) realloc( effectnames, (effectnames_count + count + 1) * sizeof( EffectDesc ) );
|
||||
}
|
||||
|
||||
memcpy( effectnames + effectnames_count, opcodes, count * sizeof( EffectRef ));
|
||||
memcpy( effectnames + effectnames_count, opcodes, count * sizeof( EffectDesc ));
|
||||
effectnames_count += count;
|
||||
effectnames[effectnames_count].Name = NULL;
|
||||
//if we merge two effect lists, then we need to sort their effect tables
|
||||
//actually, we might always want to sort this list, so there is no
|
||||
//need to do it manually (sorted table is needed if we use bsearch)
|
||||
qsort(effectnames, effectnames_count, sizeof(EffectRef), compare_effects);
|
||||
qsort(effectnames, effectnames_count, sizeof(EffectDesc), compare_effects);
|
||||
}
|
||||
|
||||
EffectQueue::EffectQueue()
|
||||
@@ -424,7 +401,7 @@ Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, EffectRef &effect_reference
|
||||
return CreateEffectCopy(oldfx, effect_reference.opcode, param1, param2);
|
||||
}
|
||||
|
||||
static EffectRef fx_unsummon_creature_ref={"UnsummonCreature",NULL,-1};
|
||||
static EffectRef fx_unsummon_creature_ref = { "UnsummonCreature", -1 };
|
||||
|
||||
Effect *EffectQueue::CreateUnsummonEffect(Effect *fx)
|
||||
{
|
||||
@@ -512,7 +489,7 @@ int EffectQueue::AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const
|
||||
switch (fx->Target) {
|
||||
case FX_TARGET_ORIGINAL:
|
||||
fx->SetPosition(self->Pos);
|
||||
|
||||
|
||||
flg = ApplyEffect( st, fx, 1 );
|
||||
if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
|
||||
if( st) {
|
||||
@@ -611,9 +588,9 @@ int EffectQueue::AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const
|
||||
case FX_TARGET_PARTY:
|
||||
all_party:
|
||||
game = core->GetGame();
|
||||
i = game->GetPartySize(true);
|
||||
i = game->GetPartySize(false);
|
||||
while(i--) {
|
||||
Actor* actor = game->GetPC( i, true );
|
||||
Actor* actor = game->GetPC( i, false );
|
||||
fx->SetPosition(actor->Pos);
|
||||
|
||||
flg = ApplyEffect( actor, fx, 1 );
|
||||
@@ -703,39 +680,17 @@ int EffectQueue::AddAllEffects(Actor* target, const Point &destination) const
|
||||
return res;
|
||||
}
|
||||
|
||||
//check if an effect has no level based resistance, but instead the dice sizes/count
|
||||
//adjusts Parameter1 (like a damage causing effect)
|
||||
inline static bool IsDicedEffect(int opcode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;diced_effects[i].Name;i++) {
|
||||
if( diced_effects[i].opcode==opcode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//there is no level based resistance, but Parameter1 cannot be precalculated
|
||||
//these effects use the Dice fields in a special way
|
||||
inline static bool IsDicedEffect2(int opcode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;diced_effects2[i].Name;i++) {
|
||||
if( diced_effects2[i].opcode==opcode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//resisted effect based on level
|
||||
inline bool check_level(Actor *target, Effect *fx)
|
||||
{
|
||||
//skip non level based effects
|
||||
if( IsDicedEffect((int) fx->Opcode)) {
|
||||
//check if an effect has no level based resistance, but instead the dice sizes/count
|
||||
//adjusts Parameter1 (like a damage causing effect)
|
||||
if( Opcodes[fx->Opcode].Flags & EFFECT_DICED ) {
|
||||
//add the caster level to the dice count
|
||||
if (fx->IsVariable) {
|
||||
fx->DiceThrown+=fx->CasterLevel;
|
||||
}
|
||||
fx->Parameter1 = DICE_ROLL((signed)fx->Parameter1);
|
||||
//this is a hack for PST style diced effects
|
||||
if( core->HasFeature(GF_SAVE_FOR_HALF) ) {
|
||||
@@ -749,7 +704,9 @@ inline bool check_level(Actor *target, Effect *fx)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if( IsDicedEffect2((int) fx->Opcode)) {
|
||||
//there is no level based resistance, but Parameter1 cannot be precalculated
|
||||
//these effects use the Dice fields in a special way
|
||||
if( Opcodes[fx->Opcode].Flags & EFFECT_NO_LEVEL_CHECK ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -770,7 +727,7 @@ inline bool check_level(Actor *target, Effect *fx)
|
||||
return false;
|
||||
}
|
||||
|
||||
//roll for the effect probability, there is a high and a low treshold, the d100
|
||||
//roll for the effect probability, there is a high and a low treshold, the d100
|
||||
//roll should hit in the middle
|
||||
inline bool check_probability(Effect* fx)
|
||||
{
|
||||
@@ -784,36 +741,36 @@ inline bool check_probability(Effect* fx)
|
||||
}
|
||||
|
||||
//immunity effects
|
||||
static EffectRef fx_level_immunity_ref={"Protection:Spelllevel",NULL,-1};
|
||||
static EffectRef fx_opcode_immunity_ref={"Protection:Opcode",NULL,-1}; //bg2
|
||||
static EffectRef fx_opcode_immunity2_ref={"Protection:Opcode2",NULL,-1};//iwd
|
||||
static EffectRef fx_spell_immunity_ref={"Protection:Spell",NULL,-1}; //bg2
|
||||
static EffectRef fx_spell_immunity2_ref={"Protection:Spell2",NULL,-1};//iwd
|
||||
static EffectRef fx_store_spell_sequencer_ref={"Sequencer:Store",NULL,-1}; //bg2, works against sequencers
|
||||
static EffectRef fx_school_immunity_ref={"Protection:School",NULL,-1};
|
||||
static EffectRef fx_secondary_type_immunity_ref={"Protection:SecondaryType",NULL,-1};
|
||||
static EffectRef fx_level_immunity_ref = { "Protection:Spelllevel", -1 };
|
||||
static EffectRef fx_opcode_immunity_ref = { "Protection:Opcode", -1 }; //bg2
|
||||
static EffectRef fx_opcode_immunity2_ref = { "Protection:Opcode2", -1 };//iwd
|
||||
static EffectRef fx_spell_immunity_ref = { "Protection:Spell", -1 }; //bg2
|
||||
static EffectRef fx_spell_immunity2_ref = { "Protection:Spell2", -1 };//iwd
|
||||
static EffectRef fx_store_spell_sequencer_ref = { "Sequencer:Store", -1 }; //bg2, works against sequencers
|
||||
static EffectRef fx_school_immunity_ref = { "Protection:School", -1 };
|
||||
static EffectRef fx_secondary_type_immunity_ref = { "Protection:SecondaryType", -1 };
|
||||
|
||||
//decrementing immunity effects
|
||||
static EffectRef fx_level_immunity_dec_ref={"Protection:SpellLevelDec",NULL,-1};
|
||||
static EffectRef fx_spell_immunity_dec_ref={"Protection:SpellDec",NULL,-1};
|
||||
static EffectRef fx_school_immunity_dec_ref={"Protection:SchoolDec",NULL,-1};
|
||||
static EffectRef fx_secondary_type_immunity_dec_ref={"Protection:SecondaryTypeDec",NULL,-1};
|
||||
static EffectRef fx_level_immunity_dec_ref = { "Protection:SpellLevelDec", -1 };
|
||||
static EffectRef fx_spell_immunity_dec_ref = { "Protection:SpellDec", -1 };
|
||||
static EffectRef fx_school_immunity_dec_ref = { "Protection:SchoolDec", -1 };
|
||||
static EffectRef fx_secondary_type_immunity_dec_ref = { "Protection:SecondaryTypeDec", -1 };
|
||||
|
||||
//bounce effects
|
||||
static EffectRef fx_level_bounce_ref={"Bounce:SpellLevel",NULL,-1};
|
||||
//static EffectRef fx_opcode_bounce_ref={"Bounce:Opcode",NULL,-1};
|
||||
static EffectRef fx_spell_bounce_ref={"Bounce:Spell",NULL,-1};
|
||||
static EffectRef fx_school_bounce_ref={"Bounce:School",NULL,-1};
|
||||
static EffectRef fx_secondary_type_bounce_ref={"Bounce:SecondaryType",NULL,-1};
|
||||
static EffectRef fx_level_bounce_ref = { "Bounce:SpellLevel", -1 };
|
||||
//static EffectRef fx_opcode_bounce_ref = { "Bounce:Opcode", -1 };
|
||||
static EffectRef fx_spell_bounce_ref = { "Bounce:Spell", -1 };
|
||||
static EffectRef fx_school_bounce_ref = { "Bounce:School", -1 };
|
||||
static EffectRef fx_secondary_type_bounce_ref = { "Bounce:SecondaryType", -1 };
|
||||
|
||||
//decrementing bounce effects
|
||||
static EffectRef fx_level_bounce_dec_ref={"Bounce:SpellLevelDec",NULL,-1};
|
||||
static EffectRef fx_spell_bounce_dec_ref={"Bounce:SpellDec",NULL,-1};
|
||||
static EffectRef fx_school_bounce_dec_ref={"Bounce:SchoolDec",NULL,-1};
|
||||
static EffectRef fx_secondary_type_bounce_dec_ref={"Bounce:SecondaryTypeDec",NULL,-1};
|
||||
static EffectRef fx_level_bounce_dec_ref = { "Bounce:SpellLevelDec", -1 };
|
||||
static EffectRef fx_spell_bounce_dec_ref = { "Bounce:SpellDec", -1 };
|
||||
static EffectRef fx_school_bounce_dec_ref = { "Bounce:SchoolDec", -1 };
|
||||
static EffectRef fx_secondary_type_bounce_dec_ref = { "Bounce:SecondaryTypeDec", -1 };
|
||||
|
||||
//spelltrap (multiple decrementing immunity)
|
||||
static EffectRef fx_spelltrap={"SpellTrap", NULL,-1};
|
||||
static EffectRef fx_spelltrap = { "SpellTrap", -1 };
|
||||
|
||||
//this is for whole spell immunity/bounce
|
||||
inline static void DecreaseEffect(Effect *efx)
|
||||
@@ -1078,6 +1035,8 @@ int EffectQueue::ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieD
|
||||
|
||||
fx->FirstApply=first_apply;
|
||||
if( first_apply) {
|
||||
if (Owner)
|
||||
fx->CasterID = Owner->GetGlobalID();
|
||||
if( (fx->PosX==0xffffffff) && (fx->PosY==0xffffffff)) {
|
||||
fx->PosX = target->Pos.x;
|
||||
fx->PosY = target->Pos.y;
|
||||
@@ -1158,6 +1117,10 @@ int EffectQueue::ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieD
|
||||
EffectFunction fn = 0;
|
||||
if( fx->Opcode<MAX_EFFECTS) {
|
||||
fn = Opcodes[fx->Opcode].Function;
|
||||
if (!(target || (Opcodes[fx->Opcode].Flags & EFFECT_NO_ACTOR))) {
|
||||
printf("targetless opcode without EFFECT_NO_ACTOR: %d, skipping\n", fx->Opcode);
|
||||
return FX_NOT_APPLIED;
|
||||
}
|
||||
}
|
||||
int res = FX_ABORT;
|
||||
if( fn) {
|
||||
@@ -1533,7 +1496,7 @@ int EffectQueue::SpecificDamageBonus(ieDword opcode, ieDword param2) const
|
||||
return bonus;
|
||||
}
|
||||
|
||||
static EffectRef fx_damage_bonus_modifier_ref={"DamageBonusModifier",NULL,-1};
|
||||
static EffectRef fx_damage_bonus_modifier_ref = { "DamageBonusModifier", -1 };
|
||||
int EffectQueue::SpecificDamageBonus(ieDword damage_type) const
|
||||
{
|
||||
ResolveEffectRef(fx_damage_bonus_modifier_ref);
|
||||
@@ -1625,7 +1588,7 @@ bool EffectQueue::WeaponImmunity(ieDword opcode, int enchantment, ieDword weapon
|
||||
return false;
|
||||
}
|
||||
|
||||
static EffectRef fx_weapon_immunity_ref={"Protection:Weapons",NULL,-1};
|
||||
static EffectRef fx_weapon_immunity_ref = { "Protection:Weapons", -1 };
|
||||
|
||||
bool EffectQueue::WeaponImmunity(int enchantment, ieDword weapontype) const
|
||||
{
|
||||
@@ -1659,7 +1622,7 @@ void EffectQueue::AddWeaponEffects(EffectQueue *fxqueue, EffectRef &fx_ref) cons
|
||||
}
|
||||
|
||||
/* no longer needed, use IE_CASTING stat
|
||||
static EffectRef fx_disable_spellcasting_ref={ "DisableCasting", NULL, -1 };
|
||||
static EffectRef fx_disable_spellcasting_ref = { "DisableCasting", -1 };
|
||||
int EffectQueue::DisabledSpellcasting(int types) const
|
||||
{
|
||||
ResolveEffectRef(fx_disable_spellcasting_ref);
|
||||
@@ -1800,7 +1763,7 @@ bool EffectQueue::HasDuration(Effect *fx)
|
||||
return false;
|
||||
}
|
||||
|
||||
static EffectRef fx_variable_ref={"Variable:StoreLocalVariable",NULL,-1};
|
||||
static EffectRef fx_variable_ref = { "Variable:StoreLocalVariable", -1 };
|
||||
|
||||
//returns true if the effect must be saved
|
||||
//variables are saved differently
|
||||
|
||||
@@ -100,6 +100,9 @@ class Scriptable;
|
||||
// You will need to get GameTime somehow to use this macro
|
||||
#define PrepareDuration(fx) fx->Duration = (fx->Duration*AI_UPDATE_TIME + GameTime)
|
||||
|
||||
//return the caster object
|
||||
#define GetCasterObject() (core->GetGame()->GetActorByGlobalID(fx->CasterID))
|
||||
|
||||
// often used stat modifications, usually Parameter2 types 0, 1 and 2
|
||||
//these macros should work differently in permanent mode (modify base too)
|
||||
#define STAT_GET(stat) (target->Modified[ stat ])
|
||||
@@ -135,19 +138,33 @@ class Scriptable;
|
||||
typedef int (* EffectFunction)(Scriptable*, Actor*, Effect*);
|
||||
|
||||
|
||||
/** Links Effect name to a function implementing the effect */
|
||||
/** Cached Effect -> opcode mapping */
|
||||
struct EffectRef {
|
||||
const char* Name;
|
||||
EffectFunction Function;
|
||||
int opcode;
|
||||
};
|
||||
|
||||
/** Links Effect name to a function implementing the effect */
|
||||
struct EffectDesc {
|
||||
const char* Name;
|
||||
EffectFunction Function;
|
||||
int Flags;
|
||||
int opcode;
|
||||
};
|
||||
|
||||
enum EffectFlags {
|
||||
EFFECT_NORMAL = 0,
|
||||
EFFECT_DICED = 1,
|
||||
EFFECT_NO_LEVEL_CHECK = 2,
|
||||
EFFECT_NO_ACTOR = 4
|
||||
};
|
||||
|
||||
/** Initializes table of available spell Effects used by all the queues. */
|
||||
/** The available effects should already be registered by the effect plugins */
|
||||
bool Init_EffectQueue();
|
||||
|
||||
/** Registers opcodes implemented by an effect plugin */
|
||||
void EffectQueue_RegisterOpcodes(int count, const EffectRef *opcodes);
|
||||
void EffectQueue_RegisterOpcodes(int count, const EffectDesc *opcodes);
|
||||
|
||||
/** release effect list when Interface is destroyed */
|
||||
void EffectQueue_ReleaseMemory();
|
||||
@@ -198,7 +215,7 @@ public:
|
||||
void RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const;
|
||||
|
||||
/* removes any effects (delayed or not) which were using projectile */
|
||||
void RemoveAllEffectsWithProjectile(ieDword projectile) const;
|
||||
void RemoveAllEffectsWithProjectile(ieDword projectile) const;
|
||||
|
||||
/* removes equipping effects with specified inventory slot code */
|
||||
void RemoveEquippingEffects(ieDwordSigned slotcode) const;
|
||||
|
||||
@@ -215,7 +215,7 @@ void Font::PrintFromLine(int startrow, Region rgn, const unsigned char* string,
|
||||
if (sscanf( tag, "color=%02X%02X%02X", &r, &g, &b ) != 3)
|
||||
continue;
|
||||
const Color c = {(unsigned char) r,(unsigned char)g, (unsigned char)b, 0};
|
||||
Palette* newPal = core->CreatePalette( c, black );
|
||||
Palette* newPal = core->CreatePalette( c, palette->back );
|
||||
sprBuffer->SetPalette( newPal );
|
||||
gamedata->FreePalette( newPal );
|
||||
continue;
|
||||
@@ -390,7 +390,7 @@ void Font::Print(Region cliprgn, Region rgn, const unsigned char* string,
|
||||
if (sscanf( tag, "color=%02X%02X%02X", &r, &g, &b ) != 3)
|
||||
continue;
|
||||
const Color c = {(unsigned char) r,(unsigned char) g,(unsigned char) b, 0};
|
||||
Palette* newPal = core->CreatePalette( c, black );
|
||||
Palette* newPal = core->CreatePalette( c, palette->back );
|
||||
sprBuffer->SetPalette( newPal );
|
||||
gamedata->FreePalette( newPal );
|
||||
continue;
|
||||
|
||||
@@ -394,7 +394,7 @@ void Button::OnMouseDown(unsigned short x, unsigned short y,
|
||||
drag_start.x = Owner->XPos + XPos + x;
|
||||
drag_start.y = Owner->YPos + YPos + y;
|
||||
|
||||
if (State == IE_GUI_BUTTON_LOCKED) {
|
||||
if (State == IE_GUI_BUTTON_LOCKED) {
|
||||
SetState( IE_GUI_BUTTON_LOCKED_PRESSED );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ void Console::OnSpecialKeyPress(unsigned char Key)
|
||||
//ctrl-up
|
||||
void Console::HistoryBack()
|
||||
{
|
||||
HistoryAdd(false);
|
||||
HistoryAdd(false);
|
||||
if (HistPos < HistMax-1 && Buffer[0]) {
|
||||
HistPos++;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ void Console::HistoryBack()
|
||||
//ctrl-down
|
||||
void Console::HistoryForward()
|
||||
{
|
||||
HistoryAdd(false);
|
||||
HistoryAdd(false);
|
||||
if (HistPos == 0) {
|
||||
Buffer[0]=0;
|
||||
CurPos=0;
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include "Video.h"
|
||||
#include "damages.h"
|
||||
#include "GameScript/GSUtils.h"
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -44,10 +47,10 @@
|
||||
#define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
|
||||
#define DEBUG_SHOW_LIGHTMAP 0x08
|
||||
|
||||
#ifndef TOUCHSCREEN
|
||||
#define SCROLL_BORDER 5
|
||||
#ifdef TOUCHSCREEN
|
||||
# define SCROLL_BORDER 32
|
||||
#else
|
||||
#define SCROLL_BORDER 32
|
||||
# define SCROLL_BORDER 5
|
||||
#endif
|
||||
|
||||
static const Color cyan = {
|
||||
@@ -73,12 +76,9 @@ static const Color black = {
|
||||
static const Color blue = {
|
||||
0x00, 0x00, 0xff, 0x80
|
||||
};
|
||||
|
||||
#ifdef TOUCHSCREEN
|
||||
static const Color gray = {
|
||||
0x80, 0x80, 0x80, 0xff
|
||||
};
|
||||
#endif
|
||||
|
||||
//Animation* effect;
|
||||
|
||||
@@ -134,11 +134,11 @@ GameControl::GameControl(void)
|
||||
scrolling = false;
|
||||
#ifdef TOUCHSCREEN
|
||||
touched=false;
|
||||
#endif
|
||||
#endif
|
||||
numScrollCursor = 0;
|
||||
DebugFlags = 0;
|
||||
AIUpdateCounter = 1;
|
||||
EnableRunning = true; //make this a game flag if you wish
|
||||
EnableRunning = true; //make this a game flag if you wish
|
||||
ieDword tmp=0;
|
||||
|
||||
ResetTargetMode();
|
||||
@@ -259,14 +259,14 @@ void GameControl::CreateMovement(Actor *actor, const Point &p)
|
||||
Action *action = NULL;
|
||||
if (DoubleClick && EnableRunning) {
|
||||
sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
|
||||
action = GenerateAction( Tmp );
|
||||
action = GenerateAction( Tmp );
|
||||
//if it didn't work don't insist
|
||||
if (!action)
|
||||
EnableRunning = false;
|
||||
}
|
||||
if (!action) {
|
||||
sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
|
||||
action = GenerateAction( Tmp );
|
||||
action = GenerateAction( Tmp );
|
||||
}
|
||||
|
||||
actor->AddAction( action );
|
||||
@@ -499,7 +499,7 @@ void GameControl::Draw(unsigned short x, unsigned short y)
|
||||
Actor *actor = area->GetActorByGlobalID(trackerID);
|
||||
|
||||
if (actor) {
|
||||
Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance);
|
||||
Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD|GA_NO_LOS, distance);
|
||||
|
||||
int i = 0;
|
||||
while(monsters[i]) {
|
||||
@@ -586,21 +586,21 @@ void GameControl::Draw(unsigned short x, unsigned short y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef TOUCHSCREEN
|
||||
if(moveY < 0 && scrolling)
|
||||
if (moveY < 0 && scrolling)
|
||||
video->DrawLine(screen.x+4, screen.y+SCROLL_BORDER, screen.w+screen.x-4, screen.y+SCROLL_BORDER, red);
|
||||
else
|
||||
video->DrawLine(screen.x+4, screen.y+SCROLL_BORDER, screen.w+screen.x-4, screen.y+SCROLL_BORDER, gray);
|
||||
if(moveY > 0 && scrolling)
|
||||
if (moveY > 0 && scrolling)
|
||||
video->DrawLine(screen.x+4, screen.h-SCROLL_BORDER, screen.w+screen.x-4, screen.h-SCROLL_BORDER, red);
|
||||
else
|
||||
video->DrawLine(screen.x+4, screen.h-SCROLL_BORDER, screen.w+screen.x-4, screen.h-SCROLL_BORDER, gray);
|
||||
if(moveX < 0 && scrolling)
|
||||
if (moveX < 0 && scrolling)
|
||||
video->DrawLine(screen.x+SCROLL_BORDER, screen.y+4, screen.x+SCROLL_BORDER, screen.h+screen.y-4, red);
|
||||
else
|
||||
video->DrawLine(screen.x+SCROLL_BORDER, screen.y+4, screen.x+SCROLL_BORDER, screen.h+screen.y-4, gray);
|
||||
if(moveX > 0 && scrolling)
|
||||
if (moveX > 0 && scrolling)
|
||||
video->DrawLine(screen.w+screen.x-SCROLL_BORDER, screen.y+4, screen.w+screen.x-SCROLL_BORDER, screen.h-4, red);
|
||||
else
|
||||
video->DrawLine(screen.w+screen.x-SCROLL_BORDER, screen.y+4, screen.w+screen.x-SCROLL_BORDER, screen.h-4, gray);
|
||||
@@ -708,8 +708,8 @@ void GameControl::SelectActor(int whom, int type)
|
||||
}
|
||||
|
||||
//Effect for the ctrl-r cheatkey (resurrect)
|
||||
static EffectRef heal_ref={"CurrentHPModifier", NULL, -1};
|
||||
static EffectRef damage_ref={"Damage", NULL, -1};
|
||||
static EffectRef heal_ref = { "CurrentHPModifier", -1 };
|
||||
static EffectRef damage_ref = { "Damage", -1 };
|
||||
|
||||
/** Key Release Event */
|
||||
void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod)
|
||||
@@ -793,9 +793,9 @@ void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod)
|
||||
if (target) {
|
||||
src->CastSpell( TestSpell, target, false );
|
||||
if (src->LastTarget) {
|
||||
src->CastSpellEnd();
|
||||
src->CastSpellEnd(0);
|
||||
} else {
|
||||
src->CastSpellPointEnd();
|
||||
src->CastSpellPointEnd(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1111,7 +1111,7 @@ void GameControl::DisplayTooltip() {
|
||||
strindex = STR_INJURED2;
|
||||
} else if (hp > maxhp/3) {
|
||||
strindex = STR_INJURED3;
|
||||
} else {
|
||||
} else {
|
||||
strindex = STR_INJURED4;
|
||||
}
|
||||
strindex = displaymsg->GetStringReference(strindex);
|
||||
@@ -1229,7 +1229,7 @@ void GameControl::OnMouseOver(unsigned short x, unsigned short y)
|
||||
if (ScreenFlags & SF_DISABLEMOUSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef TOUCHSCREEN
|
||||
int mousescrollspd = core->GetMouseScrollSpeed();
|
||||
Region region;
|
||||
@@ -1240,20 +1240,17 @@ void GameControl::OnMouseOver(unsigned short x, unsigned short y)
|
||||
moveY = 0;
|
||||
// Top scroll area
|
||||
region=Region(XPos, YPos, Width, YPos+SCROLL_BORDER);
|
||||
if(region.PointInside(x, y))
|
||||
{
|
||||
if (region.PointInside(x, y)) {
|
||||
// Check for end of map area
|
||||
if(viewport.y > 0)
|
||||
if (viewport.y > 0)
|
||||
moveY = -mousescrollspd;
|
||||
}
|
||||
// Bottom scroll area
|
||||
region=Region(XPos, Height-SCROLL_BORDER, Width, Height);
|
||||
if(region.PointInside(x, y))
|
||||
{
|
||||
if (region.PointInside(x, y)) {
|
||||
// Check for end of map area
|
||||
map = core->GetGame()->GetCurrentArea();
|
||||
if(map != NULL)
|
||||
{
|
||||
if (map != NULL) {
|
||||
mapsize = map->TMap->GetMapSize();
|
||||
if((viewport.y + viewport.h) < mapsize.y)
|
||||
moveY = mousescrollspd;
|
||||
@@ -1261,40 +1258,34 @@ void GameControl::OnMouseOver(unsigned short x, unsigned short y)
|
||||
}
|
||||
// Left scroll area
|
||||
region=Region(XPos, YPos, XPos+SCROLL_BORDER, Height);
|
||||
if(region.PointInside(x, y))
|
||||
{
|
||||
if (region.PointInside(x, y)) {
|
||||
// Check for end of map area
|
||||
if(viewport.x > 0)
|
||||
moveX = -mousescrollspd;
|
||||
}
|
||||
// Right scroll area
|
||||
region=Region(Width-SCROLL_BORDER, YPos, Width, Height);
|
||||
if(region.PointInside(x, y))
|
||||
{
|
||||
if (region.PointInside(x, y)) {
|
||||
// Check for end of map area
|
||||
map = core->GetGame()->GetCurrentArea();
|
||||
if(map != NULL)
|
||||
{
|
||||
if (map != NULL) {
|
||||
mapsize = map->TMap->GetMapSize();
|
||||
if((viewport.x + viewport.w) < mapsize.x)
|
||||
moveX = mousescrollspd;
|
||||
}
|
||||
}
|
||||
if ((moveX != 0 || moveY != 0) && touched)
|
||||
{
|
||||
if ((moveX != 0 || moveY != 0) && touched) {
|
||||
scrolling = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
moveX = 0;
|
||||
moveY = 0;
|
||||
scrolling = false;
|
||||
Video* video = core->GetVideoDriver();
|
||||
video->SetDragCursor(NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
lastMouseX = x;
|
||||
lastMouseY = y;
|
||||
Point p( x,y );
|
||||
@@ -1459,8 +1450,6 @@ end_function:
|
||||
}
|
||||
}
|
||||
|
||||
//#define SCROLL_BORDER 5
|
||||
|
||||
/** Global Mouse Move Event */
|
||||
void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
|
||||
{
|
||||
@@ -1471,6 +1460,7 @@ void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
|
||||
if (Owner->Visible!=WINDOW_VISIBLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef TOUCHSCREEN
|
||||
int mousescrollspd = core->GetMouseScrollSpeed();
|
||||
|
||||
@@ -1499,7 +1489,10 @@ void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
|
||||
Video* video = core->GetVideoDriver();
|
||||
video->SetDragCursor(NULL);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
(void)x;
|
||||
(void)y;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GameControl::UpdateScrolling() {
|
||||
@@ -1735,18 +1728,18 @@ void GameControl::HandleContainer(Container *container, Actor *actor)
|
||||
return;
|
||||
}
|
||||
|
||||
core->SetEventFlag(EF_RESETTARGET);
|
||||
|
||||
if (target_mode == TARGET_MODE_ATTACK) {
|
||||
actor->ClearPath();
|
||||
actor->ClearActions();
|
||||
snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", container->GetScriptName());
|
||||
actor->AddAction(GenerateAction(Tmp));
|
||||
ResetTargetMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((target_mode == TARGET_MODE_PICK)) {
|
||||
TryToPick(actor, container);
|
||||
ResetTargetMode();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1773,18 +1766,18 @@ void GameControl::HandleDoor(Door *door, Actor *actor)
|
||||
return;
|
||||
}
|
||||
|
||||
core->SetEventFlag(EF_RESETTARGET);
|
||||
|
||||
if (target_mode == TARGET_MODE_ATTACK) {
|
||||
actor->ClearPath();
|
||||
actor->ClearActions();
|
||||
snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", door->GetScriptName());
|
||||
actor->AddAction(GenerateAction(Tmp));
|
||||
ResetTargetMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (target_mode == TARGET_MODE_PICK) {
|
||||
TryToPick(actor, door);
|
||||
ResetTargetMode();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1807,7 +1800,6 @@ bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p)
|
||||
}
|
||||
if ((target_mode == TARGET_MODE_PICK)) {
|
||||
TryToDisarm(actor, trap);
|
||||
ResetTargetMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1885,7 +1877,7 @@ void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short
|
||||
SelectionRect.h = 0;
|
||||
#ifdef TOUCHSCREEN
|
||||
touched=true;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1912,25 +1904,21 @@ void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short B
|
||||
|
||||
#ifdef TOUCHSCREEN
|
||||
touched=false;
|
||||
if(scrolling)
|
||||
{
|
||||
if (scrolling) {
|
||||
moveX = 0;
|
||||
moveY = 0;
|
||||
scrolling=false;
|
||||
Video* video = core->GetVideoDriver();
|
||||
video->SetDragCursor(NULL);
|
||||
if (DrawSelectionRect)
|
||||
{
|
||||
if (DrawSelectionRect) {
|
||||
Actor** ab;
|
||||
unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
|
||||
if (count != 0)
|
||||
{
|
||||
if (count != 0) {
|
||||
for (i = 0; i < highlighted.size(); i++)
|
||||
highlighted[i]->SetOver( false );
|
||||
highlighted.clear();
|
||||
game->SelectActor( NULL, false, SELECT_NORMAL );
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
for (i = 0; i < count; i++) {
|
||||
// FIXME: should call handler only once
|
||||
game->SelectActor( ab[i], true, SELECT_NORMAL );
|
||||
}
|
||||
@@ -1960,7 +1948,7 @@ void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short B
|
||||
}
|
||||
|
||||
//hidden actors are not selectable by clicking on them
|
||||
Actor* actor = area->GetActor( p, GA_DEFAULT | GA_NO_DEAD | GA_NO_HIDDEN);
|
||||
Actor* actor = area->GetActor( p, GA_DEFAULT /*| GA_NO_DEAD */| GA_NO_HIDDEN | target_types);
|
||||
if (Button == GEM_MB_MENU) {
|
||||
if (actor) {
|
||||
//from GSUtils
|
||||
@@ -2013,6 +2001,7 @@ void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short B
|
||||
}
|
||||
} else {
|
||||
if (HandleActiveRegion(overInfoPoint, pc, p)) {
|
||||
core->SetEventFlag(EF_RESETTARGET);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2205,8 +2194,12 @@ void GameControl::SetTargetMode(int mode) {
|
||||
}
|
||||
|
||||
void GameControl::ResetTargetMode() {
|
||||
SetTargetMode(TARGET_MODE_NONE);
|
||||
target_types = GA_NO_DEAD|GA_NO_HIDDEN;
|
||||
SetTargetMode(TARGET_MODE_NONE);
|
||||
}
|
||||
|
||||
void GameControl::UpdateTargetMode() {
|
||||
SetTargetMode(target_mode);
|
||||
}
|
||||
|
||||
/** Special Key Press */
|
||||
@@ -2635,7 +2628,7 @@ void GameControl::ChangeMap(Actor *pc, bool forced)
|
||||
{
|
||||
//swap in the area of the actor
|
||||
Game* game = core->GetGame();
|
||||
if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) {
|
||||
if (forced || (pc && stricmp( pc->Area, game->CurrentArea) != 0) ) {
|
||||
dialoghandler->EndDialog();
|
||||
overInfoPoint = NULL;
|
||||
overContainer = NULL;
|
||||
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
//int action;
|
||||
#ifdef TOUCHSCREEN
|
||||
bool touched; // true, if player touched screen (left button down and hold)
|
||||
#endif
|
||||
#endif
|
||||
public:
|
||||
Door* overDoor;
|
||||
Container* overContainer;
|
||||
@@ -216,6 +216,7 @@ public:
|
||||
void TryToDisarm(Actor *source, InfoPoint *tgt);
|
||||
void PerformActionOn(Actor *actor);
|
||||
void ResetTargetMode();
|
||||
void UpdateTargetMode();
|
||||
|
||||
// returns the default cursor fitting the targeting mode
|
||||
int GetDefaultCursor() const;
|
||||
|
||||
@@ -67,6 +67,8 @@ WorldMapControl::WorldMapControl(const char *font, int direction)
|
||||
}
|
||||
|
||||
// initialize label colors
|
||||
// NOTE: it would be better to initialize these colors from
|
||||
// some 2da file
|
||||
Color normal = { 0xf0, 0xf0, 0xf0, 0xff };
|
||||
Color selected = { 0xf0, 0x80, 0x80, 0xff };
|
||||
Color notvisited = { 0x80, 0x80, 0xf0, 0xff };
|
||||
@@ -315,10 +317,10 @@ void WorldMapControl::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/,
|
||||
if (Button != GEM_MB_ACTION) {
|
||||
return;
|
||||
}
|
||||
MouseIsDown = false;
|
||||
if (lastCursor==IE_CURSOR_NORMAL) {
|
||||
RunEventHandler( WorldMapControlOnPress );
|
||||
}
|
||||
MouseIsDown = false;
|
||||
}
|
||||
|
||||
/** Special Key Press */
|
||||
@@ -377,19 +379,35 @@ bool WorldMapControl::SetEvent(int eventType, EventHandler handler)
|
||||
|
||||
void WorldMapControl::SetColor(int which, Color color)
|
||||
{
|
||||
Color black = { 0x00, 0x00, 0x00, 0x00 };
|
||||
Palette* pal;
|
||||
// FIXME: clearly it can cause palettes to be re-created several times,
|
||||
// because setting background color creates all palettes anew.
|
||||
switch (which) {
|
||||
case IE_GUI_WMAP_COLOR_NORMAL:
|
||||
case IE_GUI_WMAP_COLOR_BACKGROUND:
|
||||
pal = core->CreatePalette( pal_normal->front, color );
|
||||
gamedata->FreePalette( pal_normal );
|
||||
pal_normal = core->CreatePalette( color, black );
|
||||
pal_normal = pal;
|
||||
pal = core->CreatePalette( pal_selected->front, color );
|
||||
gamedata->FreePalette( pal_selected );
|
||||
pal_selected = pal;
|
||||
pal = core->CreatePalette( pal_notvisited->front, color );
|
||||
gamedata->FreePalette( pal_notvisited );
|
||||
pal_notvisited = pal;
|
||||
break;
|
||||
case IE_GUI_WMAP_COLOR_NORMAL:
|
||||
pal = core->CreatePalette( color, pal_normal->back );
|
||||
gamedata->FreePalette( pal_normal );
|
||||
pal_normal = pal;
|
||||
break;
|
||||
case IE_GUI_WMAP_COLOR_SELECTED:
|
||||
pal = core->CreatePalette( color, pal_selected->back );
|
||||
gamedata->FreePalette( pal_selected );
|
||||
pal_selected = core->CreatePalette( color, black );
|
||||
pal_selected = pal;
|
||||
break;
|
||||
case IE_GUI_WMAP_COLOR_NOTVISITED:
|
||||
pal = core->CreatePalette( color, pal_notvisited->back );
|
||||
gamedata->FreePalette( pal_notvisited );
|
||||
pal_notvisited = core->CreatePalette( color, black );
|
||||
pal_notvisited = pal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -40,9 +40,10 @@ class WorldMapControl;
|
||||
|
||||
// !!! Keep these synchronized with GUIDefines.py !!!
|
||||
/** Which label color is set with SetColor() */
|
||||
#define IE_GUI_WMAP_COLOR_NORMAL 0
|
||||
#define IE_GUI_WMAP_COLOR_SELECTED 1
|
||||
#define IE_GUI_WMAP_COLOR_NOTVISITED 2
|
||||
#define IE_GUI_WMAP_COLOR_BACKGROUND 0
|
||||
#define IE_GUI_WMAP_COLOR_NORMAL 1
|
||||
#define IE_GUI_WMAP_COLOR_SELECTED 2
|
||||
#define IE_GUI_WMAP_COLOR_NOTVISITED 3
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -635,6 +635,15 @@ Map *Game::GetMap(const char *areaname, bool change)
|
||||
area->ChangeMap(IsDay());
|
||||
ChangeSong(false, true);
|
||||
Infravision();
|
||||
|
||||
//call area customization script for PST
|
||||
//moved here because the current area is set here
|
||||
ScriptEngine *sE = core->GetGUIScriptEngine();
|
||||
if (core->HasFeature(GF_AREA_OVERRIDE) && sE) {
|
||||
//area ResRef is accessible by GemRB.GetGameString (STR_AREANAME)
|
||||
sE->RunFunction("Maze", "CustomizeArea");
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
return GetMap(index);
|
||||
@@ -738,6 +747,7 @@ int Game::LoadMap(const char* ResRef, bool loadscreen)
|
||||
unsigned int i;
|
||||
Map *newMap;
|
||||
PluginHolder<MapMgr> mM(IE_ARE_CLASS_ID);
|
||||
ScriptEngine *sE = core->GetGUIScriptEngine();
|
||||
|
||||
//this shouldn't happen
|
||||
if (!mM) {
|
||||
@@ -750,10 +760,10 @@ int Game::LoadMap(const char* ResRef, bool loadscreen)
|
||||
}
|
||||
|
||||
bool hide = false;
|
||||
if (loadscreen) {
|
||||
if (loadscreen && sE) {
|
||||
hide = core->HideGCWindow();
|
||||
core->GetGUIScriptEngine()->RunFunction("LoadScreen", "StartLoadScreen");
|
||||
core->GetGUIScriptEngine()->RunFunction("LoadScreen", "SetLoadScreen");
|
||||
sE->RunFunction("LoadScreen", "StartLoadScreen");
|
||||
sE->RunFunction("LoadScreen", "SetLoadScreen");
|
||||
}
|
||||
DataStream* ds = gamedata->GetResource( ResRef, IE_ARE_CLASS_ID );
|
||||
if (!ds) {
|
||||
@@ -766,6 +776,7 @@ int Game::LoadMap(const char* ResRef, bool loadscreen)
|
||||
if (!newMap) {
|
||||
goto failedload;
|
||||
}
|
||||
|
||||
core->LoadProgress(100);
|
||||
|
||||
for (i = 0; i < PCs.size(); i++) {
|
||||
@@ -1129,7 +1140,8 @@ void Game::IncrementChapter()
|
||||
//chapter first set to 0 (prologue)
|
||||
ieDword chapter = (ieDword) -1;
|
||||
locals->Lookup("CHAPTER",chapter);
|
||||
locals->SetAt("CHAPTER",chapter+1);
|
||||
//increment chapter only if it exists
|
||||
locals->SetAt("CHAPTER", chapter+1, core->HasFeature(GF_NO_NEW_VARIABLES) );
|
||||
//clear statistics
|
||||
for (unsigned int i=0; i<PCs.size(); i++) {
|
||||
//all PCs must have this!
|
||||
@@ -1228,33 +1240,13 @@ bool Game::PartyOverflow() const
|
||||
return (PCs.size()>partysize);
|
||||
}
|
||||
|
||||
bool Game::PCInCombat(Actor* actor) const
|
||||
{
|
||||
if (!CombatCounter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor->LastTarget) {
|
||||
return true;
|
||||
}
|
||||
if (AttackersOf(actor->GetGlobalID(), actor->GetCurrentArea())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Game::AnyPCInCombat() const
|
||||
{
|
||||
if (!CombatCounter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i=0; i<PCs.size(); i++) {
|
||||
if (PCInCombat (PCs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//returns true if the protagonist (or the whole party died)
|
||||
@@ -1297,30 +1289,10 @@ void Game::UpdateScripts()
|
||||
ProcessActions(false);
|
||||
size_t idx;
|
||||
|
||||
bool PartyAttack = false;
|
||||
PartyAttack = false;
|
||||
|
||||
for (idx=0;idx<Maps.size();idx++) {
|
||||
Maps[idx]->UpdateScripts();
|
||||
size_t acnt=Attackers.size();
|
||||
while(acnt--) {
|
||||
Actor *actor = Maps[idx]->GetActorByGlobalID(Attackers[acnt]);
|
||||
if (actor) {
|
||||
if ( !Maps[idx]->GetActorByGlobalID(actor->LastTarget) ) {
|
||||
//Actor's target left area
|
||||
OutAttack(Attackers[acnt]);
|
||||
continue;
|
||||
} else {
|
||||
//each attacker handles their own round initiation
|
||||
actor->InitRound(GameTime);
|
||||
if (actor->InParty) {
|
||||
PartyAttack = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Attacker is gone from area
|
||||
OutAttack(Attackers[acnt]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PartyAttack) {
|
||||
@@ -1626,28 +1598,6 @@ bool Game::IsDay()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Game::InAttack(ieDword globalID)
|
||||
{
|
||||
std::vector< ieDword>::const_iterator idx;
|
||||
|
||||
for(idx=Attackers.begin(); idx!=Attackers.end();idx++) {
|
||||
if (*idx==globalID) return;
|
||||
}
|
||||
Attackers.push_back(globalID);
|
||||
}
|
||||
|
||||
void Game::OutAttack(ieDword globalID)
|
||||
{
|
||||
std::vector< ieDword>::iterator idx;
|
||||
|
||||
for(idx=Attackers.begin(); idx!=Attackers.end();idx++) {
|
||||
if (*idx==globalID) {
|
||||
Attackers.erase(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::ChangeSong(bool always, bool force)
|
||||
{
|
||||
int Song;
|
||||
@@ -1666,25 +1616,6 @@ void Game::ChangeSong(bool always, bool force)
|
||||
area->PlayAreaSong( Song, always, force );
|
||||
}
|
||||
|
||||
int Game::AttackersOf(ieDword globalID, Map *area) const
|
||||
{
|
||||
if (!area) {
|
||||
return 0;
|
||||
}
|
||||
std::vector< ieDword>::const_iterator idx;
|
||||
|
||||
int cnt = 0;
|
||||
for(idx=Attackers.begin(); idx!=Attackers.end();idx++) {
|
||||
Actor * actor = area->GetActorByGlobalID(*idx);
|
||||
if (actor) {
|
||||
if (actor->LastTarget==globalID) {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* this method redraws weather. If update is false,
|
||||
then the weather particles won't change (game paused)
|
||||
*/
|
||||
@@ -1795,16 +1726,6 @@ void Game::DebugDump()
|
||||
printf("Current area: %s Previous area: %s\n", CurrentArea, PreviousArea);
|
||||
printf("Global script: %s\n", Scripts[0]->GetName());
|
||||
printf("CombatCounter: %d\n", (int) CombatCounter);
|
||||
printf("Attackers count: %d\n", (int) Attackers.size());
|
||||
for(idx=0;idx<Attackers.size(); idx++) {
|
||||
Actor *actor = GetActorByGlobalID(Attackers[idx]);
|
||||
if (!actor) {
|
||||
printf("Name: ???\n");
|
||||
continue;
|
||||
}
|
||||
Actor *whom = GetActorByGlobalID(actor->LastTarget);
|
||||
printf("Name: %s Attacking : %s\n", actor->ShortName, whom?whom->ShortName:"???");
|
||||
}
|
||||
|
||||
printf("Party size: %d\n", (int) PCs.size());
|
||||
for(idx=0;idx<PCs.size();idx++) {
|
||||
|
||||
@@ -159,20 +159,20 @@ struct GAMLocationEntry {
|
||||
|
||||
//pst maze data structures (TODO: create a separate class?)
|
||||
struct maze_entry {
|
||||
ieDword unknown00;
|
||||
ieDword override;
|
||||
ieDword accessible;
|
||||
ieDword valid;
|
||||
ieDword trapped;
|
||||
ieDword traptype;
|
||||
ieWord walls;
|
||||
ieDword unknown16;
|
||||
ieDword visited;
|
||||
};
|
||||
|
||||
struct maze_header {
|
||||
ieDword maze_sizex, maze_sizey;
|
||||
ieDword pos1x, pos1y; //nordom's position
|
||||
ieDword pos2x, pos2y; //main hall position
|
||||
ieDword pos3x, pos3y; //unknown
|
||||
ieDword pos3x, pos3y; //foyer entrance
|
||||
ieDword pos4x, pos4y; //unknown
|
||||
ieDword trapcount; //based on map size
|
||||
ieDword initialized; //set to 1
|
||||
@@ -202,18 +202,18 @@ struct maze_header {
|
||||
#define MH_UNKNOWN30 11
|
||||
|
||||
//maze entry indices
|
||||
#define ME_0 0
|
||||
#define ME_OVERRIDE 0
|
||||
#define ME_VALID 1
|
||||
#define ME_ACCESSIBLE 2
|
||||
#define ME_TRAP 3
|
||||
#define ME_WALLS 4
|
||||
#define ME_16 5
|
||||
#define ME_VISITED 5
|
||||
|
||||
//ME_WALL bitfields
|
||||
#define WALL_EAST 1
|
||||
#define WALL_WEST 2
|
||||
#define WALL_NORTH 4
|
||||
#define WALL_SOUTH 8
|
||||
#define WALL_SOUTH 1
|
||||
#define WALL_NORTH 2
|
||||
#define WALL_EAST 4
|
||||
#define WALL_WEST 8
|
||||
|
||||
#define MAX_CRLEVEL 32
|
||||
|
||||
@@ -236,7 +236,6 @@ private:
|
||||
std::vector< GAMLocationEntry*> savedpositions;
|
||||
std::vector< GAMLocationEntry*> planepositions;
|
||||
std::vector< char*> mastarea;
|
||||
std::vector< ieDword> Attackers;
|
||||
CRRow *crtable;
|
||||
ieResRef restmovies[8];
|
||||
ieResRef daymovies[8];
|
||||
@@ -286,6 +285,7 @@ public:
|
||||
EventHandler event_handler; //like in Control
|
||||
bool hasInfra;
|
||||
bool familiarBlock;
|
||||
bool PartyAttack;
|
||||
private:
|
||||
/** reads the challenge rating table */
|
||||
void LoadCRTable();
|
||||
@@ -354,9 +354,6 @@ public:
|
||||
Actor* GetNPC(unsigned int Index);
|
||||
void SwapPCs(unsigned int Index1, unsigned int Index2);
|
||||
bool IsDay();
|
||||
void InAttack(ieDword globalID);
|
||||
void OutAttack(ieDword globalID);
|
||||
int AttackersOf(ieDword globalID, Map *area) const;
|
||||
|
||||
//journal entries
|
||||
/** Deletes one or all journal entries if strref is -1 */
|
||||
@@ -409,9 +406,6 @@ public:
|
||||
}
|
||||
return Formations[WhichFormation];
|
||||
}
|
||||
size_t GetAttackerCount() const {
|
||||
return Attackers.size();
|
||||
}
|
||||
|
||||
/** converts challenge rating to xp */
|
||||
int GetXPFromCR(int cr);
|
||||
@@ -419,8 +413,6 @@ public:
|
||||
void ShareXP(int XP, int flags);
|
||||
/** returns true if we should start the party overflow window */
|
||||
bool PartyOverflow() const;
|
||||
/** returns true if actor is an attacker or being attacked */
|
||||
bool PCInCombat(Actor *actor) const;
|
||||
/** returns true if any pc is attacker or being attacked */
|
||||
bool AnyPCInCombat() const;
|
||||
/** returns true if the party death condition is true */
|
||||
|
||||
@@ -411,7 +411,7 @@ ScriptedAnimation* GameData::GetScriptedAnimation( const char *effect, bool doub
|
||||
{
|
||||
ScriptedAnimation *ret = NULL;
|
||||
|
||||
if (Exists( effect, IE_VVC_CLASS_ID ) ) {
|
||||
if (Exists( effect, IE_VVC_CLASS_ID, true ) ) {
|
||||
DataStream *ds = GetResource( effect, IE_VVC_CLASS_ID );
|
||||
ret = new ScriptedAnimation(ds, true);
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
#include "Video.h"
|
||||
#include "WorldMap.h"
|
||||
#include "GUI/GameControl.h"
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Action Functions
|
||||
@@ -549,7 +552,7 @@ void GameScript::TeleportParty(Scriptable* /*Sender*/, Action* parameters)
|
||||
int i = game->GetPartySize(false);
|
||||
while (i--) {
|
||||
Actor *tar = game->GetPC(i, false);
|
||||
MoveBetweenAreasCore( tar, parameters->string1Parameter,
|
||||
MoveBetweenAreasCore( tar, parameters->string0Parameter,
|
||||
parameters->pointParameter, -1, true);
|
||||
}
|
||||
}
|
||||
@@ -785,24 +788,24 @@ void GameScript::Ally(Scriptable* Sender, Action* /*parameters*/)
|
||||
/** GemRB extension: you can replace baldur.bcs */
|
||||
void GameScript::ChangeAIScript(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
if (parameters->int0Parameter>7) {
|
||||
return;
|
||||
}
|
||||
if (Sender->Type!=ST_ACTOR && parameters->int0Parameter) {
|
||||
if (parameters->int0Parameter>=MAX_SCRIPTS) {
|
||||
return;
|
||||
}
|
||||
//clearing the queue, and checking script level was intentionally removed
|
||||
Sender->SetScript( parameters->string0Parameter, parameters->int0Parameter, false );
|
||||
}
|
||||
|
||||
void GameScript::ForceAIScript(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
if (parameters->int0Parameter>=MAX_SCRIPTS) {
|
||||
return;
|
||||
}
|
||||
Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
|
||||
if (!tar || tar->Type != ST_ACTOR) {
|
||||
return;
|
||||
}
|
||||
Actor* actor = ( Actor* ) tar;
|
||||
//changeaiscript clears the queue, i believe
|
||||
// actor->ClearActions();
|
||||
//clearing the queue, and checking script level was intentionally removed
|
||||
actor->SetScript( parameters->string0Parameter, parameters->int0Parameter, false );
|
||||
}
|
||||
|
||||
@@ -1123,14 +1126,12 @@ void GameScript::MoveToPoint(Scriptable* Sender, Action* parameters)
|
||||
return;
|
||||
}
|
||||
Actor* actor = ( Actor* ) Sender;
|
||||
//WalkTo could release the current action, so we need this
|
||||
ieDword tmp = (ieDword) parameters->int0Parameter;
|
||||
//InMove can clear destination, so we need to save it
|
||||
Point dest = actor->Destination;
|
||||
|
||||
// try the actual move, if we are not already moving there
|
||||
if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
|
||||
actor->WalkTo( parameters->pointParameter, 0, tmp );
|
||||
actor->WalkTo( parameters->pointParameter, 0 );
|
||||
dest = actor->Destination;
|
||||
}
|
||||
|
||||
@@ -1139,19 +1140,6 @@ void GameScript::MoveToPoint(Scriptable* Sender, Action* parameters)
|
||||
// we should probably instead keep retrying until we reach dest
|
||||
Sender->ReleaseCurrentAction();
|
||||
}
|
||||
|
||||
if (tmp) {
|
||||
if (!actor->InMove()) {
|
||||
//can't reach target, movement failed
|
||||
//we have to use tmp-1 because the distance required might be 0,
|
||||
//so in GoNearAndRetry we add 1 to distance
|
||||
if (Distance(dest,actor)>tmp-1) {
|
||||
//to prevent deadlocks, we free the action
|
||||
//which caused MoveToPoint in the first place
|
||||
Sender->PopNextAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//bg2, jumps to saved location in variable
|
||||
@@ -1753,6 +1741,7 @@ void GameScript::StartMusic(Scriptable* Sender, Action* parameters)
|
||||
case 3: //force switch, but wait for previous music to end gracefully
|
||||
force = false;
|
||||
restart = true;
|
||||
break;
|
||||
default:
|
||||
force = false;
|
||||
restart = false;
|
||||
@@ -2578,14 +2567,14 @@ void GameScript::Spell(Scriptable* Sender, Action* parameters)
|
||||
|
||||
//if target was set, fire spell
|
||||
if (Sender->LastTarget) {
|
||||
Sender->CastSpellEnd();
|
||||
Sender->CastSpellEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
|
||||
//the target was converted to a point
|
||||
if(!Sender->LastTargetPos.isempty()) {
|
||||
Sender->CastSpellPointEnd();
|
||||
Sender->CastSpellPointEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
@@ -2609,8 +2598,8 @@ void GameScript::Spell(Scriptable* Sender, Action* parameters)
|
||||
Actor *act = (Actor *) Sender;
|
||||
|
||||
//move near to target
|
||||
if (dist != 0xfffffff) {
|
||||
if (PersonalDistance(tar, Sender) > dist) {
|
||||
if (dist != 0xffffffff) {
|
||||
if (PersonalDistance(tar, Sender) > dist || !Sender->GetCurrentArea()->IsVisible(Sender->Pos, tar->Pos)) {
|
||||
MoveNearerTo(Sender,tar,dist);
|
||||
return;
|
||||
}
|
||||
@@ -2647,7 +2636,7 @@ void GameScript::SpellPoint(Scriptable* Sender, Action* parameters)
|
||||
|
||||
//if target was set, fire spell
|
||||
if (!Sender->LastTargetPos.isempty()) {
|
||||
Sender->CastSpellPointEnd();
|
||||
Sender->CastSpellPointEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
@@ -2657,7 +2646,7 @@ void GameScript::SpellPoint(Scriptable* Sender, Action* parameters)
|
||||
|
||||
Actor *act = (Actor *) Sender;
|
||||
//move near to target
|
||||
if (PersonalDistance(parameters->pointParameter, Sender) > dist) {
|
||||
if (PersonalDistance(parameters->pointParameter, Sender) > dist || !Sender->GetCurrentArea()->IsVisible(Sender->Pos, parameters->pointParameter)) {
|
||||
MoveNearerTo(Sender,parameters->pointParameter,dist, 0);
|
||||
return;
|
||||
}
|
||||
@@ -2696,14 +2685,14 @@ void GameScript::SpellNoDec(Scriptable* Sender, Action* parameters)
|
||||
|
||||
//if target was set, fire spell
|
||||
if (Sender->LastTarget) {
|
||||
Sender->CastSpellEnd();
|
||||
Sender->CastSpellEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
|
||||
//the target was converted to a point
|
||||
if(!Sender->LastTargetPos.isempty()) {
|
||||
Sender->CastSpellPointEnd();
|
||||
Sender->CastSpellPointEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
@@ -2753,7 +2742,7 @@ void GameScript::SpellPointNoDec(Scriptable* Sender, Action* parameters)
|
||||
|
||||
//if target was set, fire spell
|
||||
if (!Sender->LastTargetPos.isempty()) {
|
||||
Sender->CastSpellPointEnd();
|
||||
Sender->CastSpellPointEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
@@ -2795,14 +2784,14 @@ void GameScript::ForceSpell(Scriptable* Sender, Action* parameters)
|
||||
|
||||
//if target was set, fire spell
|
||||
if (Sender->LastTarget) {
|
||||
Sender->CastSpellEnd();
|
||||
Sender->CastSpellEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
|
||||
//the target was converted to a point
|
||||
if(!Sender->LastTargetPos.isempty()) {
|
||||
Sender->CastSpellPointEnd();
|
||||
Sender->CastSpellPointEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
@@ -2851,7 +2840,7 @@ void GameScript::ForceSpellPoint(Scriptable* Sender, Action* parameters)
|
||||
|
||||
//if target was set, fire spell
|
||||
if (!Sender->LastTargetPos.isempty()) {
|
||||
Sender->CastSpellPointEnd();
|
||||
Sender->CastSpellPointEnd(0);
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
@@ -2877,10 +2866,11 @@ void GameScript::ForceSpellPoint(Scriptable* Sender, Action* parameters)
|
||||
//ForceSpell with zero casting time
|
||||
//zero casting time, no depletion, not interruptable
|
||||
//FIXME The caster must meet the level requirements as set in the spell file
|
||||
//FIXME The spell level is taken as parameter2 in some cases
|
||||
//FIXME The spell level is taken as parameter2 in some cases (FIXED)
|
||||
void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
ieResRef spellres;
|
||||
int level;
|
||||
|
||||
if (!ResolveSpellName( spellres, parameters) ) {
|
||||
Sender->ReleaseCurrentAction();
|
||||
@@ -2904,10 +2894,15 @@ void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters)
|
||||
actor->SetStance (IE_ANI_CONJURE);
|
||||
}
|
||||
Sender->CastSpell (spellres, tar, false, true);
|
||||
if (tar->Type==ST_ACTOR) {
|
||||
Sender->CastSpellEnd();
|
||||
if (parameters->string0Parameter[0]) {
|
||||
level = parameters->int0Parameter;
|
||||
} else {
|
||||
Sender->CastSpellPointEnd();
|
||||
level = parameters->int1Parameter;
|
||||
}
|
||||
if (tar->Type==ST_ACTOR) {
|
||||
Sender->CastSpellEnd(level);
|
||||
} else {
|
||||
Sender->CastSpellPointEnd(level);
|
||||
}
|
||||
Sender->ReleaseCurrentAction();
|
||||
}
|
||||
@@ -2919,6 +2914,7 @@ void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters)
|
||||
void GameScript::ReallyForceSpellPoint(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
ieResRef spellres;
|
||||
int level;
|
||||
|
||||
if (!ResolveSpellName( spellres, parameters) ) {
|
||||
Sender->ReleaseCurrentAction();
|
||||
@@ -2940,7 +2936,12 @@ void GameScript::ReallyForceSpellPoint(Scriptable* Sender, Action* parameters)
|
||||
actor->SetStance (IE_ANI_CONJURE);
|
||||
}
|
||||
Sender->CastSpellPoint (spellres, parameters->pointParameter, false, true);
|
||||
Sender->CastSpellPointEnd();
|
||||
if (parameters->string0Parameter[0]) {
|
||||
level = parameters->int0Parameter;
|
||||
} else {
|
||||
level = parameters->int1Parameter;
|
||||
}
|
||||
Sender->CastSpellPointEnd(level);
|
||||
Sender->ReleaseCurrentAction();
|
||||
}
|
||||
|
||||
@@ -2949,6 +2950,7 @@ void GameScript::ReallyForceSpellPoint(Scriptable* Sender, Action* parameters)
|
||||
void GameScript::ReallyForceSpellDead(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
ieResRef spellres;
|
||||
int level;
|
||||
|
||||
if (!ResolveSpellName( spellres, parameters) ) {
|
||||
Sender->ReleaseCurrentAction();
|
||||
@@ -2965,18 +2967,17 @@ void GameScript::ReallyForceSpellDead(Scriptable* Sender, Action* parameters)
|
||||
return;
|
||||
}
|
||||
Sender->LastTargetPos=parameters->pointParameter;
|
||||
/*
|
||||
if (Sender->Type == ST_ACTOR) {
|
||||
Actor *actor = (Actor *) Sender;
|
||||
//the dead don't wiggle their fingers
|
||||
//actor->SetStance (IE_ANI_CONJURE);
|
||||
}
|
||||
*/
|
||||
|
||||
Sender->CastSpell (spellres, tar, false, true);
|
||||
if (tar->Type==ST_ACTOR) {
|
||||
Sender->CastSpellEnd();
|
||||
if (parameters->string0Parameter[0]) {
|
||||
level = parameters->int0Parameter;
|
||||
} else {
|
||||
Sender->CastSpellPointEnd();
|
||||
level = parameters->int1Parameter;
|
||||
}
|
||||
if (tar->Type==ST_ACTOR) {
|
||||
Sender->CastSpellEnd(parameters->int1Parameter);
|
||||
} else {
|
||||
Sender->CastSpellPointEnd(parameters->int1Parameter);
|
||||
}
|
||||
Sender->ReleaseCurrentAction();
|
||||
}
|
||||
@@ -3200,10 +3201,17 @@ void GameScript::JoinParty(Scriptable* Sender, Action* parameters)
|
||||
if (Sender->Type != ST_ACTOR) {
|
||||
return;
|
||||
}
|
||||
// make sure we're in the same area, otherwise Dynaheir joins when Minsc does
|
||||
// but she's in another area and needs to be rescued first!
|
||||
Actor* act = ( Actor* ) Sender;
|
||||
Game *game = core->GetGame();
|
||||
if (act->GetCurrentArea() != game->GetCurrentArea()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* calling this, so it is simpler to change */
|
||||
/* i'm not sure this is required here at all */
|
||||
SetBeenInPartyFlags(Sender, parameters);
|
||||
Actor* act = ( Actor* ) Sender;
|
||||
act->SetBase( IE_EA, EA_PC );
|
||||
if (core->HasFeature( GF_HAS_DPLAYER )) {
|
||||
/* we must reset various existing scripts */
|
||||
@@ -3222,7 +3230,7 @@ void GameScript::JoinParty(Scriptable* Sender, Action* parameters)
|
||||
act->SetDialog( resref );
|
||||
}
|
||||
}
|
||||
core->GetGame()->JoinParty( act, JP_JOIN );
|
||||
game->JoinParty( act, JP_JOIN );
|
||||
}
|
||||
|
||||
void GameScript::LeaveParty(Scriptable* Sender, Action* /*parameters*/)
|
||||
@@ -4076,7 +4084,7 @@ void GameScript::UnloadArea(Scriptable* /*Sender*/, Action* parameters)
|
||||
}
|
||||
}
|
||||
|
||||
static EffectRef fx_death_ref={"Death", NULL, -1};
|
||||
static EffectRef fx_death_ref = { "Death", -1 };
|
||||
void GameScript::Kill(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
|
||||
@@ -4185,7 +4193,7 @@ void GameScript::TakePartyItem(Scriptable* Sender, Action* parameters)
|
||||
Game *game=core->GetGame();
|
||||
int i=game->GetPartySize(false);
|
||||
while (i--) {
|
||||
int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0,IE_INV_ITEM_UNSTEALABLE);
|
||||
int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE,IE_INV_ITEM_UNSTEALABLE);
|
||||
if (res!=MIC_NOITEM) return;
|
||||
}
|
||||
}
|
||||
@@ -4197,7 +4205,7 @@ void GameScript::TakePartyItemNum(Scriptable* Sender, Action* parameters)
|
||||
Game *game=core->GetGame();
|
||||
int i=game->GetPartySize(false);
|
||||
while (i--) {
|
||||
int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0, IE_INV_ITEM_UNSTEALABLE);
|
||||
int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE, IE_INV_ITEM_UNSTEALABLE);
|
||||
if (res == MIC_GOTITEM) {
|
||||
i++;
|
||||
count--;
|
||||
@@ -4213,7 +4221,7 @@ void GameScript::TakePartyItemRange(Scriptable* Sender, Action* parameters)
|
||||
while (i--) {
|
||||
Actor *ac = game->GetPC(i,false);
|
||||
if (Distance(Sender, ac)<MAX_OPERATING_DISTANCE) {
|
||||
while (MoveItemCore(ac, Sender, parameters->string0Parameter,0,IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { }
|
||||
while (MoveItemCore(ac, Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE,IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4223,7 +4231,7 @@ void GameScript::TakePartyItemAll(Scriptable* Sender, Action* parameters)
|
||||
Game *game=core->GetGame();
|
||||
int i=game->GetPartySize(false);
|
||||
while (i--) {
|
||||
while (MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0, IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { }
|
||||
while (MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE, IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5232,9 +5240,11 @@ void GameScript::RemoveSpell( Scriptable* Sender, Action* parameters)
|
||||
}
|
||||
Actor *actor = (Actor *) Sender;
|
||||
if (parameters->string0Parameter[0]) {
|
||||
type = parameters->int1Parameter;
|
||||
} else {
|
||||
//the spell resref is in the string parameter
|
||||
type = parameters->int0Parameter;
|
||||
} else {
|
||||
//the spell number is in the int0 parameter
|
||||
type = parameters->int1Parameter;
|
||||
}
|
||||
if (type==2) {
|
||||
//remove spell from both book and memorization
|
||||
@@ -5548,6 +5558,13 @@ void GameScript::UseContainer(Scriptable* Sender, Action* /*parameters*/)
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if (core->InCutSceneMode()) {
|
||||
//cannot use container in dialog or cutscene
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
|
||||
Actor *actor = (Actor *)Sender;
|
||||
Container *container = core->GetCurrentContainer();
|
||||
if (!container) {
|
||||
@@ -5623,7 +5640,9 @@ void GameScript::FixEngineRoom(Scriptable* Sender, Action* /*parameters*/)
|
||||
int value = CheckVariable( Sender, "EnginInMaze","GLOBAL");
|
||||
if (value) {
|
||||
SetVariable(Sender, "EnginInMaze", "GLOBAL", 0);
|
||||
core->SetEventFlag(EF_CREATEMAZE);
|
||||
//this works only because the engine room exit depends only on the EnginInMaze variable
|
||||
ScriptEngine *sE = core->GetGUIScriptEngine();
|
||||
sE->RunFunction("Maze", "CustomizeArea");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6983,7 +7002,7 @@ void GameScript::SpellCastEffect(Scriptable* Sender, Action* parameters)
|
||||
//this action plays a vvc animation over target
|
||||
//we simply apply the appropriate opcode on the target (see iwdopcodes)
|
||||
//the list of vvcs is in iwdshtab.2da
|
||||
EffectRef fx_iwd_visual_spell_hit_ref={"IWDVisualSpellHit",NULL,-1};
|
||||
static EffectRef fx_iwd_visual_spell_hit_ref = { "IWDVisualSpellHit", -1 };
|
||||
|
||||
void GameScript::SpellHitEffectSprite(Scriptable* Sender, Action* parameters)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#include "TileMap.h"
|
||||
#include "Video.h"
|
||||
#include "GUI/GameControl.h"
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
@@ -57,6 +60,9 @@ int ObjectIDSCount = 7;
|
||||
int MaxObjectNesting = 5;
|
||||
bool HasAdditionalRect = false;
|
||||
bool HasTriggerPoint = false;
|
||||
//don't create new variables
|
||||
bool NoCreate = false;
|
||||
bool HasKaputz = false;
|
||||
//released by ReleaseMemory
|
||||
ieResRef *ObjectIDSTableNames;
|
||||
int ObjectFieldsCount = 7;
|
||||
@@ -700,6 +706,8 @@ void ChangeAnimationCore(Actor *src, const char *resref, bool effect)
|
||||
map->AddActor( tar );
|
||||
Point pos = src->Pos;
|
||||
tar->SetOrientation(src->GetOrientation(), false );
|
||||
// make sure to copy the HP, to avoid things like magically-healing trolls
|
||||
tar->BaseStats[IE_HITPOINTS]=src->BaseStats[IE_HITPOINTS];
|
||||
src->DestroySelf();
|
||||
// can't SetPosition while the old actor is taking the spot
|
||||
tar->SetPosition(pos, 1);
|
||||
@@ -1133,15 +1141,19 @@ void MoveToObjectCore(Scriptable *Sender, Action *parameters, ieDword flags, boo
|
||||
|
||||
void CreateItemCore(CREItem *item, const char *resref, int a, int b, int c)
|
||||
{
|
||||
strncpy(item->ItemResRef, resref, 8);
|
||||
//copy the whole resref, including the terminating zero
|
||||
strnuprcpy(item->ItemResRef, resref, 8);
|
||||
core->ResolveRandomItem(item);
|
||||
if (a==-1) {
|
||||
Item *origitem = gamedata->GetItem(resref);
|
||||
for(int i=0;i<3;i++) {
|
||||
ITMExtHeader *e = origitem->GetExtHeader(i);
|
||||
item->Usages[i]=e?e->Charges:0;
|
||||
//use the default charge counts of the item
|
||||
Item *origitem = gamedata->GetItem(item->ItemResRef);
|
||||
if (origitem) {
|
||||
for(int i=0;i<3;i++) {
|
||||
ITMExtHeader *e = origitem->GetExtHeader(i);
|
||||
item->Usages[i]=e?e->Charges:0;
|
||||
}
|
||||
gamedata->FreeItem(origitem, item->ItemResRef, false);
|
||||
}
|
||||
gamedata->FreeItem(origitem, resref, false);
|
||||
} else {
|
||||
item->Usages[0]=(ieWord) a;
|
||||
item->Usages[1]=(ieWord) b;
|
||||
@@ -1207,7 +1219,8 @@ void AttackCore(Scriptable *Sender, Scriptable *target, int flags)
|
||||
actor->SetTarget( target );
|
||||
}
|
||||
if ( Sender->GetCurrentArea()!=target->GetCurrentArea() ||
|
||||
(PersonalDistance(Sender, target) > wi.range) ) {
|
||||
(PersonalDistance(Sender, target) > wi.range) ||
|
||||
(!Sender->GetCurrentArea()->IsVisible(Sender->Pos, target->Pos))) {
|
||||
MoveNearerTo(Sender, target, wi.range);
|
||||
return;
|
||||
} else if (target->Type == ST_DOOR) {
|
||||
@@ -1567,7 +1580,7 @@ void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance)
|
||||
|
||||
myarea = Sender->GetCurrentArea();
|
||||
hisarea = target->GetCurrentArea();
|
||||
if (hisarea!=myarea) {
|
||||
if (hisarea && hisarea!=myarea) {
|
||||
target = myarea->GetTileMap()->GetTravelTo(hisarea->GetScriptName());
|
||||
|
||||
if (!target) {
|
||||
@@ -1575,9 +1588,9 @@ void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance)
|
||||
Sender->ReleaseCurrentAction();
|
||||
return;
|
||||
}
|
||||
((Actor *) Sender)->UseExit(true);
|
||||
((Actor *) Sender)->UseExit(target->GetGlobalID());
|
||||
} else {
|
||||
((Actor *) Sender)->UseExit(false);
|
||||
((Actor *) Sender)->UseExit(0);
|
||||
}
|
||||
// we deliberately don't try GetLikelyPosition here for now,
|
||||
// maybe a future idea if we have a better implementation
|
||||
@@ -1606,6 +1619,10 @@ int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int dont_rele
|
||||
return 0;
|
||||
}
|
||||
|
||||
//chasing is unbreakable
|
||||
//TODO: is this true?
|
||||
Sender->CurrentActionInterruptable = false;
|
||||
|
||||
Actor *actor = (Actor *)Sender;
|
||||
|
||||
if (!actor->InMove() || actor->Destination != p) {
|
||||
@@ -1622,33 +1639,7 @@ int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int dont_rele
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance)
|
||||
{
|
||||
Point p;
|
||||
GetPositionFromScriptable(target,p,flag);
|
||||
GoNearAndRetry(Sender, p, distance);
|
||||
}
|
||||
|
||||
void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance)
|
||||
{
|
||||
if (!Sender->GetCurrentAction() ) {
|
||||
printMessage("GameScript","NULL action retried???\n",LIGHT_RED);
|
||||
return;
|
||||
}
|
||||
Sender->AddActionInFront( Sender->GetCurrentAction() );
|
||||
char Tmp[256];
|
||||
sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y );
|
||||
Action * action = GenerateAction( Tmp);
|
||||
//experimental hack, this value means,
|
||||
//MoveToPoint shall pop the next action too if movement fails
|
||||
//and the actor is farther than distance
|
||||
//this will prevent deadlocks
|
||||
//(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint)
|
||||
action->int0Parameter = distance+1;
|
||||
Sender->AddActionInFront( action );
|
||||
}
|
||||
*/
|
||||
void FreeSrc(SrcVector *poi, const ieResRef key)
|
||||
{
|
||||
int res = SrcCache.DecRef((void *) poi, key, true);
|
||||
@@ -1892,18 +1883,19 @@ void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, i
|
||||
printf( "Setting variable(\"%s%s\", %d)\n", Context,
|
||||
VarName, value );
|
||||
}
|
||||
|
||||
strncpy( newVarName, Context, 6 );
|
||||
newVarName[6]=0;
|
||||
if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
|
||||
Sender->GetCurrentArea()->locals->SetAt( VarName, value );
|
||||
Sender->GetCurrentArea()->locals->SetAt( VarName, value, NoCreate );
|
||||
return;
|
||||
}
|
||||
if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
|
||||
Sender->locals->SetAt( VarName, value );
|
||||
Sender->locals->SetAt( VarName, value, NoCreate );
|
||||
return;
|
||||
}
|
||||
Game *game = core->GetGame();
|
||||
if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
|
||||
if (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
|
||||
game->kaputz->SetAt( VarName, value );
|
||||
return;
|
||||
}
|
||||
@@ -1911,7 +1903,7 @@ void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, i
|
||||
if (strnicmp(newVarName,"GLOBAL",6) ) {
|
||||
Map *map=game->GetMap(game->FindMap(newVarName));
|
||||
if (map) {
|
||||
map->locals->SetAt( VarName, value);
|
||||
map->locals->SetAt( VarName, value, NoCreate);
|
||||
}
|
||||
else if (InDebug&ID_VARIABLES) {
|
||||
printMessage("GameScript"," ",YELLOW);
|
||||
@@ -1919,7 +1911,7 @@ void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, i
|
||||
}
|
||||
}
|
||||
else {
|
||||
game->locals->SetAt( VarName, ( ieDword ) value );
|
||||
game->locals->SetAt( VarName, ( ieDword ) value, NoCreate );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1940,22 +1932,22 @@ void SetVariable(Scriptable* Sender, const char* VarName, ieDword value)
|
||||
strncpy( newVarName, VarName, 6 );
|
||||
newVarName[6]=0;
|
||||
if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
|
||||
Sender->GetCurrentArea()->locals->SetAt( poi, value );
|
||||
Sender->GetCurrentArea()->locals->SetAt( poi, value, NoCreate );
|
||||
return;
|
||||
}
|
||||
if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
|
||||
Sender->locals->SetAt( poi, value );
|
||||
Sender->locals->SetAt( poi, value, NoCreate );
|
||||
return;
|
||||
}
|
||||
Game *game = core->GetGame();
|
||||
if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
|
||||
game->kaputz->SetAt( poi, value );
|
||||
if (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
|
||||
game->kaputz->SetAt( poi, value, NoCreate );
|
||||
return;
|
||||
}
|
||||
if (strnicmp(newVarName,"GLOBAL",6) ) {
|
||||
Map *map=game->GetMap(game->FindMap(newVarName));
|
||||
if (map) {
|
||||
map->locals->SetAt( poi, value);
|
||||
map->locals->SetAt( poi, value, NoCreate);
|
||||
}
|
||||
else if (InDebug&ID_VARIABLES) {
|
||||
printMessage("GameScript"," ",YELLOW);
|
||||
@@ -1963,7 +1955,7 @@ void SetVariable(Scriptable* Sender, const char* VarName, ieDword value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
game->locals->SetAt( poi, ( ieDword ) value );
|
||||
game->locals->SetAt( poi, ( ieDword ) value, NoCreate );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1996,7 +1988,7 @@ ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid)
|
||||
return value;
|
||||
}
|
||||
Game *game = core->GetGame();
|
||||
if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
|
||||
if (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
|
||||
game->kaputz->Lookup( poi, value );
|
||||
if (InDebug&ID_VARIABLES) {
|
||||
printf("CheckVariable %s: %d\n",VarName, value);
|
||||
@@ -2047,7 +2039,7 @@ ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Conte
|
||||
return value;
|
||||
}
|
||||
Game *game = core->GetGame();
|
||||
if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
|
||||
if (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
|
||||
game->kaputz->Lookup( VarName, value );
|
||||
if (InDebug&ID_VARIABLES) {
|
||||
printf("CheckVariable %s%s: %d\n",Context, VarName, value);
|
||||
@@ -2173,7 +2165,7 @@ Point GetEntryPoint(const char *areaname, const char *entryname)
|
||||
}
|
||||
|
||||
/* returns a spell's casting distance, it depends on the caster (level), and targeting mode too
|
||||
the used header is calculated from the caster level */
|
||||
the used header is calculated from the caster level */
|
||||
unsigned int GetSpellDistance(const ieResRef spellres, Scriptable *Sender)
|
||||
{
|
||||
unsigned int dist;
|
||||
@@ -2192,11 +2184,11 @@ unsigned int GetSpellDistance(const ieResRef spellres, Scriptable *Sender)
|
||||
}
|
||||
|
||||
gamedata->FreeSpell(spl, spellres, false);
|
||||
return dist*15;
|
||||
return dist*5; //FIXME: this empirical constant shouldn't be needed!
|
||||
}
|
||||
|
||||
/* returns an item's casting distance, it depends on the used header, and targeting mode too
|
||||
the used header is explictly given */
|
||||
the used header is explictly given */
|
||||
unsigned int GetItemDistance(const ieResRef itemres, int header)
|
||||
{
|
||||
unsigned int dist;
|
||||
|
||||
@@ -51,6 +51,8 @@ extern int ObjectIDSCount;
|
||||
extern int MaxObjectNesting;
|
||||
extern bool HasAdditionalRect;
|
||||
extern bool HasTriggerPoint;
|
||||
extern bool NoCreate;
|
||||
extern bool HasKaputz;
|
||||
extern ieResRef *ObjectIDSTableNames;
|
||||
extern int ObjectFieldsCount;
|
||||
extern int ExtraParametersCount;
|
||||
@@ -90,8 +92,6 @@ void EscapeAreaCore(Scriptable *Sender, const Point &p, const char *area, const
|
||||
void GoNear(Scriptable *Sender, const Point &p);
|
||||
void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance);
|
||||
int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int no_release);
|
||||
void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool destination, int distance);
|
||||
void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance);
|
||||
|
||||
#define NO_OPERATION -1
|
||||
#define LESS_OR_EQUALS 0
|
||||
@@ -110,8 +110,8 @@ void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance);
|
||||
|
||||
GEM_EXPORT int GetGroup(Actor *actor);
|
||||
|
||||
void FreeSrc(SrcVector *poi, const ieResRef key);
|
||||
SrcVector *LoadSrc(const ieResRef resname);
|
||||
GEM_EXPORT void FreeSrc(SrcVector *poi, const ieResRef key);
|
||||
GEM_EXPORT SrcVector *LoadSrc(const ieResRef resname);
|
||||
Action *ParamCopy(Action *parameters);
|
||||
Action *ParamCopyNoOverride(Action *parameters);
|
||||
void SetVariable(Scriptable* Sender, const char* VarName, ieDword value);
|
||||
|
||||
@@ -204,6 +204,7 @@ static const TriggerLink triggernames[] = {
|
||||
{"isactive", GameScript::IsActive, 0},
|
||||
{"isanimationid", GameScript::AnimationID, 0},
|
||||
{"iscreatureareaflag", GameScript::IsCreatureAreaFlag, 0},
|
||||
{"iscreaturehiddeninshadows", GameScript::IsCreatureHiddenInShadows, 0},
|
||||
{"isfacingobject", GameScript::IsFacingObject, 0},
|
||||
{"isfacingsavedrotation", GameScript::IsFacingSavedRotation, 0},
|
||||
{"isgabber", GameScript::IsGabber, 0},
|
||||
@@ -1254,6 +1255,10 @@ void Targets::Clear()
|
||||
/** releasing global memory */
|
||||
static void CleanupIEScript()
|
||||
{
|
||||
triggersTable.release();
|
||||
actionsTable.release();
|
||||
objectsTable.release();
|
||||
overrideActionsTable.release();
|
||||
if (ObjectIDSTableNames)
|
||||
free(ObjectIDSTableNames);
|
||||
ObjectIDSTableNames = NULL;
|
||||
@@ -1281,6 +1286,9 @@ void InitializeIEScript()
|
||||
|
||||
PluginMgr::Get()->RegisterCleanup(CleanupIEScript);
|
||||
|
||||
NoCreate = core->HasFeature(GF_NO_NEW_VARIABLES);
|
||||
HasKaputz = core->HasFeature(GF_HAS_KAPUTZ);
|
||||
|
||||
InitScriptTables();
|
||||
int tT = core->LoadSymbol( "trigger" );
|
||||
int aT = core->LoadSymbol( "action" );
|
||||
@@ -1721,8 +1729,9 @@ static Object* DecodeObject(const char* line)
|
||||
printf("%s\n", origline);
|
||||
}
|
||||
//let the object realize it has no future (in case of null objects)
|
||||
if (oB->ReadyToDie()) {
|
||||
oB = NULL;
|
||||
if (oB->isNull()) {
|
||||
oB->Release();
|
||||
return NULL;
|
||||
}
|
||||
return oB;
|
||||
}
|
||||
@@ -1841,13 +1850,10 @@ bool GameScript::Update(bool *continuing, bool *done)
|
||||
}
|
||||
continueExecution = ( rB->responseSet->Execute(MySelf) != 0);
|
||||
if (continuing) *continuing = continueExecution;
|
||||
//clear triggers after response executed
|
||||
//MySelf->ClearTriggers();
|
||||
if (!continueExecution) {
|
||||
if (done) *done = true;
|
||||
break;
|
||||
}
|
||||
//continueExecution = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -2315,8 +2321,8 @@ Action* GenerateActionDirect(char *String, Scriptable *object)
|
||||
return action;
|
||||
}
|
||||
|
||||
/** Self-destructing object if it is empty */
|
||||
bool Object::ReadyToDie()
|
||||
/** Return true if object is null */
|
||||
bool Object::isNull()
|
||||
{
|
||||
if (objectName[0]!=0) {
|
||||
return false;
|
||||
@@ -2329,8 +2335,6 @@ bool Object::ReadyToDie()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//commit suicide
|
||||
Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,11 +104,11 @@ class GameScript;
|
||||
#define MAX_NESTING 5
|
||||
|
||||
#define GSASSERT(f,c) \
|
||||
if(!(f)) \
|
||||
{ \
|
||||
printf("Assertion failed: %s [0x%08lX] Line %d",#f, c, __LINE__); \
|
||||
abort(); \
|
||||
}
|
||||
if(!(f)) \
|
||||
{ \
|
||||
printf("Assertion failed: %s [0x%08lX] Line %d",#f, c, __LINE__); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
typedef std::vector<ieDword> SrcVector;
|
||||
|
||||
@@ -192,7 +192,7 @@ public:
|
||||
canary = 0xdddddddd;
|
||||
delete this;
|
||||
}
|
||||
bool ReadyToDie();
|
||||
bool isNull();
|
||||
};
|
||||
|
||||
class GEM_EXPORT Trigger {
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "Interface.h"
|
||||
#include "Game.h"
|
||||
#include "TileMap.h"
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
/* return a Targets object with a single scriptable inside */
|
||||
inline static Targets* ReturnScriptableAsTarget(Scriptable *sc)
|
||||
@@ -77,7 +80,7 @@ inline static Targets *DoObjectFiltering(Scriptable *Sender, Targets *tgts, Obje
|
||||
return tgts;
|
||||
}
|
||||
|
||||
static EffectRef fx_protection_creature_ref = { "Protection:Creature", NULL, -1 };
|
||||
static EffectRef fx_protection_creature_ref = { "Protection:Creature", -1 };
|
||||
|
||||
inline static bool DoObjectChecks(Map *map, Scriptable *Sender, Actor *target, int &dist, bool ignoreinvis=false)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#include "Video.h"
|
||||
#include "GUI/GameControl.h"
|
||||
#include "math.h" //needs for acos
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Trigger Functions
|
||||
@@ -1129,8 +1132,8 @@ int GameScript::Acquired(Scriptable * Sender, Trigger* parameters)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** this trigger accepts a numeric parameter, this number could be: */
|
||||
/** 0 - normal, 1 - equipped, 2 - identified, 3 - equipped&identified */
|
||||
/** this trigger accepts a numeric parameter, this number is the same as inventory flags
|
||||
like: 1 - identified, 2 - unstealable, 4 - stolen, 8 - undroppable, etc. */
|
||||
/** this is a GemRB extension */
|
||||
int GameScript::PartyHasItem(Scriptable * /*Sender*/, Trigger* parameters)
|
||||
{
|
||||
@@ -1512,8 +1515,8 @@ int GameScript::NearLocation(Scriptable* Sender, Trigger* parameters)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// should this be PersonalDistance?
|
||||
int distance = Distance(parameters->pointParameter, scr);
|
||||
//personaldistance is needed for modron constructs in PST maze
|
||||
int distance = PersonalDistance(parameters->pointParameter, scr);
|
||||
if (distance <= ( parameters->int0Parameter * 10 )) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ void GlobalTimer::Freeze()
|
||||
if (!game) {
|
||||
return;
|
||||
}
|
||||
game->RealTime+=advance;
|
||||
game->RealTime++;
|
||||
|
||||
ieDword count = advance/interval;
|
||||
// pst/bg2 do this, if you fix it for another game, wrap it in a check
|
||||
@@ -141,7 +141,7 @@ void GlobalTimer::DoStep(int count)
|
||||
video->MoveViewportTo(x,y);
|
||||
}
|
||||
|
||||
void GlobalTimer::Update()
|
||||
bool GlobalTimer::Update()
|
||||
{
|
||||
Map *map;
|
||||
Game *game;
|
||||
@@ -159,12 +159,12 @@ void GlobalTimer::Update()
|
||||
|
||||
if (!startTime) {
|
||||
startTime = thisTime;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
advance = thisTime - startTime;
|
||||
if ( advance < interval) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
ieDword count = advance/interval;
|
||||
DoStep(count);
|
||||
@@ -187,15 +187,16 @@ void GlobalTimer::Update()
|
||||
map->UpdateEffects();
|
||||
if (thisTime) {
|
||||
//this measures in-world time (affected by effects, actions, etc)
|
||||
game->AdvanceTime(count);
|
||||
game->AdvanceTime(1);
|
||||
}
|
||||
}
|
||||
//this measures time spent in the game (including pauses)
|
||||
if (thisTime) {
|
||||
game->RealTime+=advance;
|
||||
game->RealTime++;
|
||||
}
|
||||
end:
|
||||
startTime = thisTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
public:
|
||||
void Init();
|
||||
void Freeze();
|
||||
void Update();
|
||||
bool Update();
|
||||
bool ViewportIsMoving();
|
||||
void DoStep(int count);
|
||||
void SetMoveViewPort(ieDword x, ieDword y, int spd, bool center);
|
||||
|
||||
@@ -61,15 +61,18 @@
|
||||
#include "SpellMgr.h"
|
||||
#include "StoreMgr.h"
|
||||
#include "StringMgr.h"
|
||||
#include "SymbolMgr.h"
|
||||
#include "TileMap.h"
|
||||
#include "Video.h"
|
||||
#include "WorldMapMgr.h"
|
||||
#include "GameScript/GameScript.h"
|
||||
#include "GUI/Button.h"
|
||||
#include "GUI/Console.h"
|
||||
#include "GUI/GameControl.h"
|
||||
#include "GUI/Label.h"
|
||||
#include "GUI/MapControl.h"
|
||||
#include "GUI/WorldMapControl.h"
|
||||
#include "Scriptable/Container.h"
|
||||
#include "System/FileStream.h"
|
||||
#include "System/VFS.h"
|
||||
|
||||
@@ -166,6 +169,7 @@ Interface::Interface(int iargc, char* iargv[])
|
||||
|
||||
mousescrollspd = 10;
|
||||
|
||||
strncpy( GameType, "auto", sizeof( GameType )-1);
|
||||
ConsolePopped = false;
|
||||
CheatFlag = false;
|
||||
FogOfWar = 1;
|
||||
@@ -181,6 +185,7 @@ Interface::Interface(int iargc, char* iargv[])
|
||||
DrawFPS = false;
|
||||
KeepCache = false;
|
||||
TooltipDelay = 100;
|
||||
IgnoreOriginalINI = 0;
|
||||
FullScreen = 0;
|
||||
GUIScriptsPath[0] = 0;
|
||||
GamePath[0] = 0;
|
||||
@@ -590,6 +595,19 @@ void Interface::HandleEvents()
|
||||
guiscript->RunFunction( "Maze", "CreateMaze", false );
|
||||
return;
|
||||
}
|
||||
|
||||
if ((EventFlag&EF_RESETTARGET) && gc) {
|
||||
EventFlag&=~EF_RESETTARGET;
|
||||
EventFlag|=EF_TARGETMODE;
|
||||
gc->ResetTargetMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((EventFlag&EF_TARGETMODE) && gc) {
|
||||
EventFlag&=~EF_TARGETMODE;
|
||||
gc->UpdateTargetMode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle main loop events that might destroy or create windows
|
||||
@@ -1409,6 +1427,8 @@ int Interface::Init()
|
||||
printStatus( "OK", LIGHT_GREEN );
|
||||
|
||||
if (!LoadConfig()) {
|
||||
printMessage( "Core", "Could not load config file ", YELLOW);
|
||||
printStatus( "ERROR", LIGHT_RED );
|
||||
return GEM_ERROR;
|
||||
}
|
||||
printMessage( "Core", "Starting Plugin Manager...\n", WHITE );
|
||||
@@ -1488,13 +1508,15 @@ int Interface::Init()
|
||||
gamedata->AddSource(path, "Data", PLUGIN_RESOURCE_DIRECTORY);
|
||||
|
||||
//IWD2 movies are on the CD but not in the BIF
|
||||
char *description = strdup("CD1/data");
|
||||
for (i = 0; i < MAX_CD; i++) {
|
||||
for (size_t j=0;j<CD[i].size();j++) {
|
||||
char description[] = {'C', 'D', '1'+i, '/', 'd', 'a', 't', 'a', '\0'};
|
||||
description[2]='1'+i;
|
||||
PathJoin( path, CD[i][j].c_str(), GameDataPath, NULL);
|
||||
gamedata->AddSource(path, description, PLUGIN_RESOURCE_DIRECTORY);
|
||||
}
|
||||
}
|
||||
free(description);
|
||||
|
||||
printStatus( "OK", LIGHT_GREEN );
|
||||
}
|
||||
@@ -1510,27 +1532,47 @@ int Interface::Init()
|
||||
printStatus( "OK", LIGHT_GREEN );
|
||||
}
|
||||
|
||||
printMessage( "Core", "Reading Game Options...\n", WHITE );
|
||||
if (!LoadGemRBINI())
|
||||
printMessage( "Core", "Initializing GUI Script Engine...", WHITE );
|
||||
guiscript = PluginHolder<ScriptEngine>(IE_GUI_SCRIPT_CLASS_ID);
|
||||
if (guiscript == NULL) {
|
||||
printStatus( "ERROR", LIGHT_RED );
|
||||
return GEM_ERROR;
|
||||
}
|
||||
if (!guiscript->Init()) {
|
||||
printStatus( "ERROR", LIGHT_RED );
|
||||
return GEM_ERROR;
|
||||
}
|
||||
printStatus( "OK", LIGHT_GREEN );
|
||||
strcpy( NextScript, "Start" );
|
||||
|
||||
{
|
||||
// re-set the gemrb override path, since we now have the correct GameType if 'auto' was used
|
||||
char path[_MAX_PATH];
|
||||
PathJoin( path, GemRBOverridePath, "override", GameType, NULL);
|
||||
gamedata->AddSource(path, "GemRB Override", PLUGIN_RESOURCE_DIRECTORY, RM_REPLACE_SAME_SOURCE);
|
||||
}
|
||||
|
||||
printMessage( "Core", "Reading Game Options...\n", WHITE );
|
||||
if (!LoadGemRBINI()) {
|
||||
printf( "Cannot Load INI\nTermination in Progress...\n" );
|
||||
return GEM_ERROR;
|
||||
}
|
||||
|
||||
//loading baldur.ini
|
||||
{
|
||||
if (!IgnoreOriginalINI) {
|
||||
char ini_path[_MAX_PATH];
|
||||
PathJoin( ini_path, GamePath, INIConfig, NULL );
|
||||
LoadINI( ini_path );
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (INIConfig[i] == '.')
|
||||
break;
|
||||
GameNameResRef[i] = INIConfig[i];
|
||||
}
|
||||
GameNameResRef[i] = 0;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (INIConfig[i] == '.')
|
||||
break;
|
||||
GameNameResRef[i] = INIConfig[i];
|
||||
}
|
||||
GameNameResRef[i] = 0;
|
||||
|
||||
printMessage( "Core", "Creating Projectile Server...\n", WHITE );
|
||||
projserv = new ProjectileServer();
|
||||
if (!projserv->GetHighestProjectileNumber()) {
|
||||
@@ -1602,18 +1644,6 @@ int Interface::Init()
|
||||
return GEM_ERROR;
|
||||
}
|
||||
printStatus( "OK", LIGHT_GREEN );
|
||||
printMessage( "Core", "Initializing GUI Script Engine...", WHITE );
|
||||
guiscript = PluginHolder<ScriptEngine>(IE_GUI_SCRIPT_CLASS_ID);
|
||||
if (guiscript == NULL) {
|
||||
printStatus( "ERROR", LIGHT_RED );
|
||||
return GEM_ERROR;
|
||||
}
|
||||
if (!guiscript->Init()) {
|
||||
printStatus( "ERROR", LIGHT_RED );
|
||||
return GEM_ERROR;
|
||||
}
|
||||
printStatus( "OK", LIGHT_GREEN );
|
||||
strcpy( NextScript, "Start" );
|
||||
|
||||
int ret = LoadSprites();
|
||||
if (ret) return ret;
|
||||
@@ -2054,36 +2084,11 @@ void Interface::SetFeature(int flag, int position)
|
||||
} else {
|
||||
GameFeatures[position>>5] &= ~(1<<(position&31) );
|
||||
}
|
||||
/*
|
||||
if (position>=32) {
|
||||
position-=32;
|
||||
if (flag) {
|
||||
GameFeatures2 |= 1 << position;
|
||||
} else {
|
||||
GameFeatures2 &= ~( 1 << position );
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
GameFeatures |= 1 << position;
|
||||
} else {
|
||||
GameFeatures &= ~( 1 << position );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
ieDword Interface::HasFeature(int position) const
|
||||
{
|
||||
return GameFeatures[position>>5] & (1<<(position&31));
|
||||
/*
|
||||
if (position>=64) {
|
||||
return GameFeatures3 & ( 1 << (position-64) );
|
||||
}
|
||||
if (position>=32) {
|
||||
return GameFeatures2 & ( 1 << (position-32) );
|
||||
}
|
||||
return GameFeatures & ( 1 << position );
|
||||
*/
|
||||
}
|
||||
|
||||
/** Search directories and load a config file */
|
||||
@@ -2192,38 +2197,51 @@ bool Interface::LoadConfig(const char* filename)
|
||||
printf("%s ", filename);
|
||||
config = fopen( filename, "rb" );
|
||||
if (config == NULL) {
|
||||
printStatus("NOT FOUND", LIGHT_RED);
|
||||
printStatus("NOT FOUND", YELLOW);
|
||||
return false;
|
||||
}
|
||||
char name[65], value[_MAX_PATH + 3];
|
||||
|
||||
char line[1024];
|
||||
char *name, *nameend, *value, *valueend;
|
||||
//once GemRB own format is working well, this might be set to 0
|
||||
SaveAsOriginal = 1;
|
||||
|
||||
int lineno = 0;
|
||||
while (!feof( config )) {
|
||||
char rem;
|
||||
|
||||
if (fread( &rem, 1, 1, config ) != 1)
|
||||
if (! fgets( line, sizeof(line), config )) { // also if len == size(line)
|
||||
break;
|
||||
}
|
||||
lineno++;
|
||||
|
||||
if (rem == '#') {
|
||||
//it should always return 0
|
||||
if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0)
|
||||
break;
|
||||
// skip leading blanks from name
|
||||
name = line;
|
||||
name += strspn( line, " \t\r\n" );
|
||||
|
||||
// ignore empty or comment lines
|
||||
if (*name == '\0' || *name == '#') {
|
||||
continue;
|
||||
}
|
||||
fseek( config, -1, SEEK_CUR );
|
||||
memset(value,'\0',_MAX_PATH + 3);
|
||||
//the * element is not counted
|
||||
if (fscanf( config, "%64[^= ] = %[^\r\n]%*[\r\n]", name, value )!=2)
|
||||
|
||||
value = strchr( name, '=' );
|
||||
if (!value || value == name) {
|
||||
printf( "Invalid line %d\n", lineno );
|
||||
continue;
|
||||
for (i=_MAX_PATH + 2; i > 0; i--) {
|
||||
if (value[i] == '\0') continue;
|
||||
if (value[i] == ' ') {
|
||||
value[i] = '\0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// trim trailing blanks from name
|
||||
nameend = value;
|
||||
while (nameend > name && strchr( "= \t", *nameend )) {
|
||||
*nameend-- = '\0';
|
||||
}
|
||||
|
||||
value++;
|
||||
// skip leading blanks
|
||||
value += strspn( value, " \t");
|
||||
|
||||
// trim trailing blanks from value
|
||||
valueend = value + strlen( value ) - 1;
|
||||
while (valueend >= value && strchr( " \t\r\n", *valueend )) {
|
||||
*valueend-- = '\0';
|
||||
}
|
||||
|
||||
if (false) {
|
||||
@@ -2249,6 +2267,7 @@ bool Interface::LoadConfig(const char* filename)
|
||||
CONFIG_INT("SkipIntroVideos", SkipIntroVideos = );
|
||||
CONFIG_INT("TooltipDelay", TooltipDelay = );
|
||||
CONFIG_INT("Width", Width = );
|
||||
CONFIG_INT("IgnoreOriginalINI", IgnoreOriginalINI = );
|
||||
#undef CONFIG_INT
|
||||
#define CONFIG_STRING(str, var) \
|
||||
} else if (stricmp(name, str) == 0) { \
|
||||
@@ -2483,6 +2502,8 @@ static const char *game_flags[GF_COUNT+1]={
|
||||
"CastingSounds", //57GF_CASTING_SOUNDS
|
||||
"EnhancedCastingSounds", //58GF_CASTING_SOUNDS2
|
||||
"ForceAreaScript", //59GF_FORCE_AREA_SCRIPT
|
||||
"AreaOverride", //60GF_AREA_OVERRIDE
|
||||
"NoNewVariables", //61GF_NO_NEW_VARIABLES
|
||||
NULL //for our own safety, this marks the end of the pole
|
||||
};
|
||||
|
||||
@@ -2626,6 +2647,8 @@ bool Interface::LoadGemRBINI()
|
||||
Palette* Interface::CreatePalette(const Color &color, const Color &back)
|
||||
{
|
||||
Palette* pal = new Palette();
|
||||
pal->front = color;
|
||||
pal->back = back;
|
||||
pal->col[0].r = 0;
|
||||
pal->col[0].g = 0xff;
|
||||
pal->col[0].b = 0;
|
||||
@@ -2706,7 +2729,7 @@ ScriptEngine* Interface::GetGUIScriptEngine() const
|
||||
return guiscript.get();
|
||||
}
|
||||
|
||||
static EffectRef fx_summon_disable_ref={"AvatarRemovalModifier",NULL,-1};
|
||||
static EffectRef fx_summon_disable_ref = { "AvatarRemovalModifier", -1 };
|
||||
|
||||
//NOTE: if there were more summoned creatures, it will return only the last
|
||||
Actor *Interface::SummonCreature(const ieResRef resource, const ieResRef vvcres, Scriptable *Owner, Actor *target, const Point &position, int eamod, int level, Effect *fx, bool sexmod)
|
||||
@@ -3219,7 +3242,7 @@ void Interface::GameLoop(void)
|
||||
update_scripts = !(gc->GetDialogueFlags() & DF_FREEZE_SCRIPTS);
|
||||
}
|
||||
|
||||
GSUpdate(update_scripts);
|
||||
update_scripts = GSUpdate(update_scripts);
|
||||
|
||||
//i'm not sure if this should be here
|
||||
|
||||
@@ -3252,8 +3275,12 @@ void Interface::HandleGUIBehaviour(void)
|
||||
ieDword var = (ieDword) -3;
|
||||
vars->Lookup("DialogChoose", var);
|
||||
if ((int) var == -2) {
|
||||
// TODO: this seems to never be called? (EndDialog is called from elsewhere instead)
|
||||
gc->dialoghandler->EndDialog();
|
||||
} else if ( (int)var !=-3) {
|
||||
if ( (int) var == -1) {
|
||||
guiscript->RunFunction( "GUIWORLD", "DialogStarted" );
|
||||
}
|
||||
gc->dialoghandler->DialogChoose(var);
|
||||
if (!(gc->GetDialogueFlags() & (DF_OPENCONTINUEWINDOW | DF_OPENENDWINDOW)))
|
||||
guiscript->RunFunction( "GUIWORLD", "NextDialogState" );
|
||||
@@ -3818,7 +3845,12 @@ bool Interface::LoadINI(const char* filename)
|
||||
void Interface::SetCutSceneMode(bool active)
|
||||
{
|
||||
GameControl *gc = GetGameControl();
|
||||
|
||||
if (gc) {
|
||||
// don't mess with controls/etc if we're already in a cutscene
|
||||
if (active == (gc->GetScreenFlags()&SF_CUTSCENE))
|
||||
return;
|
||||
|
||||
gc->SetCutSceneMode( active );
|
||||
}
|
||||
if (game) {
|
||||
@@ -3832,9 +3864,26 @@ void Interface::SetCutSceneMode(bool active)
|
||||
video->SetMouseEnabled(!active);
|
||||
}
|
||||
|
||||
/** returns true if in dialogue or cutscene */
|
||||
bool Interface::InCutSceneMode() const
|
||||
{
|
||||
return (GetGameControl()->GetScreenFlags()&SF_DISABLEMOUSE)!=0;
|
||||
GameControl *gc = GetGameControl();
|
||||
if (!gc || (gc->GetDialogueFlags()&DF_IN_DIALOG) || (gc->GetScreenFlags()&SF_DISABLEMOUSE) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Updates the Game Script Engine State */
|
||||
bool Interface::GSUpdate(bool update_scripts)
|
||||
{
|
||||
if(update_scripts) {
|
||||
return timer->Update();
|
||||
}
|
||||
else {
|
||||
timer->Freeze();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::QuitGame(int BackToMain)
|
||||
@@ -4580,7 +4629,13 @@ bool Interface::ReadRandomItems()
|
||||
CREItem *Interface::ReadItem(DataStream *str)
|
||||
{
|
||||
CREItem *itm = new CREItem();
|
||||
if (ReadItem(str, itm)) return itm;
|
||||
delete itm;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CREItem *Interface::ReadItem(DataStream *str, CREItem *itm)
|
||||
{
|
||||
str->ReadResRef( itm->ItemResRef );
|
||||
str->ReadWord( &itm->Expired );
|
||||
str->ReadWord( &itm->Usages[0] );
|
||||
@@ -4590,7 +4645,6 @@ CREItem *Interface::ReadItem(DataStream *str)
|
||||
if (ResolveRandomItem(itm) ) {
|
||||
return itm;
|
||||
}
|
||||
delete itm;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -4858,19 +4912,24 @@ void Interface::PlaySound(int index)
|
||||
|
||||
Actor *Interface::GetFirstSelectedPC(bool forced)
|
||||
{
|
||||
Actor *ret = NULL;
|
||||
int slot = 0;
|
||||
int partySize = game->GetPartySize( false );
|
||||
if (!partySize) return NULL;
|
||||
for (int i = 0; i < partySize; i++) {
|
||||
Actor* actor = game->GetPC( i,false );
|
||||
if (actor->IsSelected()) {
|
||||
return actor;
|
||||
if (actor->InParty<slot || !ret) {
|
||||
ret = actor;
|
||||
slot = actor->InParty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forced) {
|
||||
return game->GetPC(0,false);
|
||||
if (forced && !ret) {
|
||||
return game->FindPC((unsigned int) 0);
|
||||
}
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Actor *Interface::GetFirstSelectedActor()
|
||||
@@ -4925,8 +4984,8 @@ void Interface::ApplySpell(const ieResRef resname, Actor *actor, Scriptable *cas
|
||||
return;
|
||||
}
|
||||
|
||||
level = spell->GetHeaderIndexFromLevel(level);
|
||||
EffectQueue *fxqueue = spell->GetEffectBlock(caster, actor->Pos, level);
|
||||
int header = spell->GetHeaderIndexFromLevel(level);
|
||||
EffectQueue *fxqueue = spell->GetEffectBlock(caster, actor->Pos, header, level);
|
||||
|
||||
ApplyEffectQueue(fxqueue, actor, caster, actor->Pos);
|
||||
delete fxqueue;
|
||||
@@ -4938,9 +4997,9 @@ void Interface::ApplySpellPoint(const ieResRef resname, Map* area, const Point &
|
||||
if (!spell) {
|
||||
return;
|
||||
}
|
||||
level = spell->GetHeaderIndexFromLevel(level);
|
||||
Projectile *pro = spell->GetProjectile(caster, level, pos);
|
||||
pro->SetCaster(caster->GetGlobalID());
|
||||
int header = spell->GetHeaderIndexFromLevel(level);
|
||||
Projectile *pro = spell->GetProjectile(caster, header, pos);
|
||||
pro->SetCaster(caster->GetGlobalID(), level);
|
||||
area->AddProjectile(pro, caster->Pos, pos);
|
||||
}
|
||||
|
||||
@@ -4982,7 +5041,7 @@ int Interface::ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *
|
||||
}
|
||||
fxqueue->SetOwner( caster );
|
||||
|
||||
if (fxqueue->AddAllEffects( actor, p )==FX_NOT_APPLIED) {
|
||||
if (fxqueue->AddAllEffects( actor, p)==FX_NOT_APPLIED) {
|
||||
res=0;
|
||||
}
|
||||
}
|
||||
@@ -5322,7 +5381,7 @@ int Interface::Autopause(ieDword flag)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Interface::RegisterOpcodes(int count, const EffectRef *opcodes)
|
||||
void Interface::RegisterOpcodes(int count, const EffectDesc *opcodes)
|
||||
{
|
||||
EffectQueue_RegisterOpcodes(count, opcodes);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class Control;
|
||||
class DataFileMgr;
|
||||
struct Effect;
|
||||
class EffectQueue;
|
||||
struct EffectRef;
|
||||
struct EffectDesc;
|
||||
class EventMgr;
|
||||
class Factory;
|
||||
class Font;
|
||||
@@ -201,6 +201,8 @@ public:
|
||||
#define EF_OPENSTORE 256 //open store window
|
||||
#define EF_EXPANSION 512 //upgrade game request
|
||||
#define EF_CREATEMAZE 1024 //call the maze generator
|
||||
#define EF_RESETTARGET 2048 //reset the mouse cursor
|
||||
#define EF_TARGETMODE 4096 //update the mouse cursor
|
||||
|
||||
//autopause
|
||||
#define AP_UNUSABLE 0
|
||||
@@ -493,15 +495,7 @@ public:
|
||||
/** returns true if in cutscene mode */
|
||||
bool InCutSceneMode() const;
|
||||
/** Updates the Game Script Engine State */
|
||||
void GSUpdate(bool update_scripts)
|
||||
{
|
||||
if(update_scripts) {
|
||||
timer->Update();
|
||||
}
|
||||
else {
|
||||
timer->Freeze();
|
||||
}
|
||||
}
|
||||
bool GSUpdate(bool update_scripts);
|
||||
/** Get the Party INI Interpreter */
|
||||
DataFileMgr * GetPartyINI() const
|
||||
{
|
||||
@@ -586,6 +580,7 @@ public:
|
||||
int GetDraggedPortrait() const { return DraggedPortrait; }
|
||||
void SetDraggedPortrait(int dp, int cursor=-1);
|
||||
CREItem *ReadItem(DataStream *str);
|
||||
CREItem *ReadItem(DataStream *str, CREItem *itm);
|
||||
bool ResolveRandomItem(CREItem *itm);
|
||||
ieStrRef GetRumour(const ieResRef resname);
|
||||
Container *GetCurrentContainer();
|
||||
@@ -653,7 +648,7 @@ public:
|
||||
/** receives an autopause reason, returns 1 if pause was triggered by this call, -1 if it was already triggered */
|
||||
int Autopause(ieDword reason);
|
||||
/** registers engine opcodes */
|
||||
void RegisterOpcodes(int count, const EffectRef *opcodes);
|
||||
void RegisterOpcodes(int count, const EffectDesc *opcodes);
|
||||
/** reads a list of resrefs into an array, returns array size */
|
||||
int ReadResRefTable(const ieResRef tablename, ieResRef *&data);
|
||||
/** frees the data */
|
||||
@@ -686,8 +681,8 @@ public:
|
||||
static const char *GetDeathVarFormat();
|
||||
int CheckSpecialSpell(ieResRef resref, Actor *actor);
|
||||
int GetSpecialSpell(ieResRef resref);
|
||||
int GetSpecialSpellsCount() { return SpecialSpellsCount; };
|
||||
SpellDescType *GetSpecialSpells() { return SpecialSpells; };
|
||||
int GetSpecialSpellsCount() { return SpecialSpellsCount; }
|
||||
SpellDescType *GetSpecialSpells() { return SpecialSpells; }
|
||||
private:
|
||||
int LoadSprites();
|
||||
bool LoadConfig(void);
|
||||
@@ -748,6 +743,7 @@ public:
|
||||
std::vector<std::string> ModPath;
|
||||
int Width, Height, Bpp, ForceStereo;
|
||||
unsigned int TooltipDelay;
|
||||
int IgnoreOriginalINI;
|
||||
unsigned int FogOfWar;
|
||||
bool CaseSensitive, GameOnCD, SkipIntroVideos, DrawFPS;
|
||||
bool GUIEnhancements;
|
||||
|
||||
@@ -892,6 +892,26 @@ bool Inventory::DropItemAtLocation(const char *resref, unsigned int flags, Map *
|
||||
if (resref[0])
|
||||
break;
|
||||
}
|
||||
|
||||
//dropping gold too
|
||||
if (!resref[0]) {
|
||||
if (Owner->Type==ST_ACTOR) {
|
||||
Actor *act = (Actor *) Owner;
|
||||
if (! act->BaseStats[IE_GOLD]) {
|
||||
return dropped;
|
||||
}
|
||||
CREItem *gold = new CREItem();
|
||||
|
||||
gold->Expired=0;
|
||||
gold->Flags=0;
|
||||
gold->Usages[1]=0;
|
||||
gold->Usages[2]=0;
|
||||
memcpy(gold->ItemResRef, core->GoldResRef, sizeof(ieResRef) );
|
||||
gold->Usages[0] = act->BaseStats[IE_GOLD];
|
||||
act->BaseStats[IE_GOLD] = 0;
|
||||
map->AddItemToLocation(loc, gold);
|
||||
}
|
||||
}
|
||||
return dropped;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ EffectQueue *Item::GetEffectBlock(Scriptable *self, const Point &pos, int usage,
|
||||
for (int i=0;i<count;i++) {
|
||||
Effect *fx = features+i;
|
||||
fx->InventorySlot = invslot;
|
||||
fx->CasterLevel = ITEM_CASTERLEVEL; //items all have casterlevel 10
|
||||
if (usage >= 0) {
|
||||
//this is not coming from the item header, but from the recharge flags
|
||||
fx->SourceFlags = ext_headers[usage].RechargeFlags;
|
||||
@@ -219,7 +220,7 @@ Projectile *Item::GetProjectile(Scriptable *self, int header, const Point &targe
|
||||
}
|
||||
|
||||
//this is the implementation of the weapon glow effect in PST
|
||||
static EffectRef glow_ref ={"Color:PulseRGB",NULL,-1};
|
||||
static EffectRef glow_ref = { "Color:PulseRGB", -1 };
|
||||
//this type of colour uses PAL32, a PST specific palette
|
||||
#define PALSIZE 32
|
||||
static Color ActorColor[PALSIZE];
|
||||
|
||||
@@ -99,6 +99,8 @@ class Projectile;
|
||||
#define CHG_NOSOUND 2
|
||||
#define CHG_DAY 3
|
||||
|
||||
//items caster level is hardcoded to 10
|
||||
#define ITEM_CASTERLEVEL 10
|
||||
/**
|
||||
* @class ITMExtHeader
|
||||
* Header for special effects and uses of an Item.
|
||||
|
||||
@@ -83,7 +83,10 @@ libgemrb_core_la_SOURCES = \
|
||||
SaveGameMgr.cpp \
|
||||
ScriptEngine.cpp \
|
||||
Scriptable/Actor.cpp \
|
||||
Scriptable/ActorBlock.cpp \
|
||||
Scriptable/Container.cpp \
|
||||
Scriptable/Door.cpp \
|
||||
Scriptable/InfoPoint.cpp \
|
||||
Scriptable/Scriptable.cpp \
|
||||
Scriptable/PCStatStruct.cpp \
|
||||
ScriptedAnimation.cpp \
|
||||
SoundMgr.cpp \
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "Interface.h"
|
||||
#include "MapMgr.h"
|
||||
#include "MusicMgr.h"
|
||||
#include "ImageMgr.h"
|
||||
#include "Palette.h"
|
||||
#include "Particles.h"
|
||||
#include "PathFinder.h"
|
||||
@@ -44,6 +45,9 @@
|
||||
#include "strrefs.h"
|
||||
#include "GameScript/GSUtils.h"
|
||||
#include "GUI/GameControl.h"
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
@@ -103,7 +107,8 @@ inline static AnimationObjectType SelectObject(Actor *actor, int q, AreaAnimatio
|
||||
|
||||
int aah;
|
||||
if (a) {
|
||||
aah = a->Pos.y;//+a->height;
|
||||
//aah = a->Pos.y;//+a->height;
|
||||
aah = a->GetHeight();
|
||||
} else {
|
||||
aah = 0x7fffffff;
|
||||
}
|
||||
@@ -227,8 +232,8 @@ void InitPathFinder()
|
||||
void AddLOS(int destx, int desty, int slot)
|
||||
{
|
||||
for (int i=0;i<MaxVisibility;i++) {
|
||||
int x=(destx*i+MaxVisibility/2)/MaxVisibility*16;
|
||||
int y=(desty*i+MaxVisibility/2)/MaxVisibility*12;
|
||||
int x = ((destx*i + MaxVisibility/2) / MaxVisibility) * 16;
|
||||
int y = ((desty*i + MaxVisibility/2) / MaxVisibility) * 12;
|
||||
if (LargeFog) {
|
||||
x += 16;
|
||||
y += 12;
|
||||
@@ -327,6 +332,8 @@ Map::Map(void)
|
||||
VisibleBitmap = NULL;
|
||||
version = 0;
|
||||
MasterArea = core->GetGame()->MasterArea(scriptName);
|
||||
Background = NULL;
|
||||
BgDuration = 0;
|
||||
}
|
||||
|
||||
Map::~Map(void)
|
||||
@@ -432,6 +439,9 @@ void Map::AddTileMap(TileMap* tm, Image* lm, Bitmap* sr, Sprite2D* sm, Bitmap* h
|
||||
SrchMap[y*Width+x] = Passable[sr->GetAt(x,y)&PATH_MAP_AREAMASK];
|
||||
}
|
||||
}
|
||||
|
||||
//delete the original searchmap
|
||||
delete sr;
|
||||
}
|
||||
|
||||
void Map::MoveToNewArea(const char *area, const char *entrance, unsigned int direction, int EveryOne, Actor *actor)
|
||||
@@ -621,8 +631,9 @@ void Map::UpdateScripts()
|
||||
// possibly wrong (so if you have problems, revert this and find
|
||||
// another way)
|
||||
if (has_pcs) {
|
||||
//Run the Map Script
|
||||
ExecuteScript( 1 );
|
||||
//Run all the Map Scripts (as in the original)
|
||||
//The default area script is in the last slot anyway
|
||||
ExecuteScript( MAX_SCRIPTS );
|
||||
}
|
||||
|
||||
//Execute Pending Actions
|
||||
@@ -660,8 +671,9 @@ void Map::UpdateScripts()
|
||||
actor->fxqueue.Cleanup();
|
||||
|
||||
//if the actor is immobile, don't run the scripts
|
||||
//FIXME: this is not universaly true, only some states have this effect
|
||||
if (!game->StateOverrideFlag && !game->StateOverrideTime) {
|
||||
if (actor->Immobile() || actor->GetStat(IE_STATE_ID) & STATE_SLEEP) {
|
||||
if (/*actor->Immobile() ||*/ actor->GetStat(IE_STATE_ID) & STATE_SLEEP) {
|
||||
actor->no_more_steps = true;
|
||||
continue;
|
||||
}
|
||||
@@ -781,17 +793,15 @@ void Map::UpdateScripts()
|
||||
if (!ip)
|
||||
break;
|
||||
//If this InfoPoint has no script and it is not a Travel Trigger, skip it
|
||||
bool wasActive = (ip->Scripts[0] || ( ip->Type == ST_TRAVEL ));
|
||||
// InfoPoints of all types don't run scripts if TRAP_DEACTIVATED is set
|
||||
// (eg, TriggerActivation changes this, see lightning room from SoA)
|
||||
if (wasActive)
|
||||
wasActive = !(ip->Flags&TRAP_DEACTIVATED);
|
||||
int wasActive = (!(ip->Flags&TRAP_DEACTIVATED) ) || (ip->Type==ST_TRAVEL);
|
||||
|
||||
//If this InfoPoint is a Switch Trigger
|
||||
if (ip->Type == ST_TRIGGER) {
|
||||
//Check if this InfoPoint was activated
|
||||
if (ip->LastTrigger) {
|
||||
if (wasActive) {
|
||||
if (wasActive && ip->Scripts[0]) {
|
||||
//Run the InfoPoint script
|
||||
ip->ExecuteScript( 1 );
|
||||
}
|
||||
@@ -814,6 +824,7 @@ void Map::UpdateScripts()
|
||||
if(ip->Entered(actor)) {
|
||||
//if trap triggered, then mark actor
|
||||
actor->SetInTrap(ipCount);
|
||||
wasActive|=TRAP_USEPOINT;
|
||||
}
|
||||
} else {
|
||||
//ST_TRAVEL
|
||||
@@ -834,6 +845,10 @@ void Map::UpdateScripts()
|
||||
|
||||
if (wasActive) {
|
||||
ip->ExecuteScript( 1 );
|
||||
//Play the PST specific enter sound
|
||||
if (wasActive&TRAP_USEPOINT) {
|
||||
core->GetAudioDrv()->Play(ip->EnterWav, ip->TrapLaunch.x, ip->TrapLaunch.y);
|
||||
}
|
||||
}
|
||||
//Execute Pending Actions
|
||||
ip->ProcessActions(false);
|
||||
@@ -877,7 +892,7 @@ bool Map::DoStepForActor(Actor *actor, int speed, ieDword time) {
|
||||
}
|
||||
|
||||
void Map::ClearSearchMapFor( Movable *actor ) {
|
||||
Actor** nearActors = GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, MAX_CIRCLE_SIZE*2*16);
|
||||
Actor** nearActors = GetAllActorsInRadius(actor->Pos, GA_NO_DEAD|GA_NO_LOS, MAX_CIRCLE_SIZE*2*16);
|
||||
BlockSearchMap( actor->Pos, actor->size, PATH_MAP_FREE);
|
||||
|
||||
// Restore the searchmap areas of any nearby actors that could
|
||||
@@ -1040,20 +1055,33 @@ void Map::DrawMap(Region screen)
|
||||
INISpawn->CheckSpawn();
|
||||
}
|
||||
|
||||
int rain;
|
||||
if (HasWeather()) {
|
||||
//zero when the weather particles are all gone
|
||||
rain = game->weather->GetPhase()-P_EMPTY;
|
||||
} else {
|
||||
rain = 0;
|
||||
}
|
||||
TMap->DrawOverlays( screen, rain );
|
||||
|
||||
//Blit the Background Map Animations (before actors)
|
||||
Video* video = core->GetVideoDriver();
|
||||
int bgoverride = false;
|
||||
|
||||
//Draw Outlines
|
||||
DrawHighlightables( screen );
|
||||
if (Background) {
|
||||
if (BgDuration<gametime) {
|
||||
video->FreeSprite(Background);
|
||||
} else {
|
||||
video->BlitSprite(Background,0,0,true);
|
||||
bgoverride = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bgoverride) {
|
||||
int rain;
|
||||
if (HasWeather()) {
|
||||
//zero when the weather particles are all gone
|
||||
rain = game->weather->GetPhase()-P_EMPTY;
|
||||
} else {
|
||||
rain = 0;
|
||||
}
|
||||
|
||||
TMap->DrawOverlays( screen, rain );
|
||||
|
||||
//Draw Outlines
|
||||
DrawHighlightables( screen );
|
||||
}
|
||||
|
||||
Region vp = video->GetViewport();
|
||||
//if it is only here, then the scripting will fail?
|
||||
@@ -1224,23 +1252,20 @@ void Map::DrawSearchMap(const Region &screen)
|
||||
}
|
||||
|
||||
//adding animation in order, based on its height parameter
|
||||
void Map::AddAnimation(AreaAnimation* anim)
|
||||
void Map::AddAnimation(AreaAnimation* panim)
|
||||
{
|
||||
//this hack is to make sure animations flagged with background
|
||||
//are always drawn first (-9999 seems sufficiently small)
|
||||
if (anim->Flags&A_ANI_BACKGROUND) {
|
||||
anim->height=-9999;
|
||||
}
|
||||
//copy external memory to core memory for msvc's sake
|
||||
AreaAnimation *anim = new AreaAnimation();
|
||||
memcpy(anim, panim, sizeof(AreaAnimation) );
|
||||
|
||||
anim->InitAnimation();
|
||||
|
||||
aniIterator iter;
|
||||
for(iter=animations.begin(); (iter!=animations.end()) && ((*iter)->height<anim->height); iter++) ;
|
||||
animations.insert(iter, anim);
|
||||
/*
|
||||
Animation *a = anim->animation[0];
|
||||
anim->SetSpriteCover(BuildSpriteCover(anim->Pos.x, anim->Pos.y,-a->animArea.x,
|
||||
-a->animArea.y, a->animArea.w, a->animArea.h,0
|
||||
));
|
||||
*/
|
||||
|
||||
int Height = anim->GetHeight();
|
||||
printf("Adding %s at height %d, Pos: %d.%d\n", anim->Name, Height, anim->Pos.x, anim->Pos.y);
|
||||
for(iter=animations.begin(); (iter!=animations.end()) && ((*iter)->GetHeight()<Height); iter++) ;
|
||||
animations.insert(iter, anim);
|
||||
}
|
||||
|
||||
//reapplying all of the effects on the actors of this map
|
||||
@@ -1482,6 +1507,7 @@ Actor* Map::GetActorInRadius(const Point &p, int flags, unsigned int radius)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//maybe consider using a simple list
|
||||
Actor **Map::GetAllActorsInRadius(const Point &p, int flags, unsigned int radius)
|
||||
{
|
||||
ieDword count = 1;
|
||||
@@ -1500,6 +1526,11 @@ Actor **Map::GetAllActorsInRadius(const Point &p, int flags, unsigned int radius
|
||||
if (!actor->Schedule(gametime, true) ) {
|
||||
continue;
|
||||
}
|
||||
if (!(flags&GA_NO_LOS)) {
|
||||
if (!IsVisible(actor->Pos, p)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -1517,6 +1548,12 @@ Actor **Map::GetAllActorsInRadius(const Point &p, int flags, unsigned int radius
|
||||
if (!actor->Schedule(gametime, true) ) {
|
||||
continue;
|
||||
}
|
||||
if (!(flags&GA_NO_LOS)) {
|
||||
if (!IsVisible(actor->Pos, p)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ret[j++]=actor;
|
||||
}
|
||||
|
||||
@@ -1729,7 +1766,7 @@ unsigned int Map::GetBlocked(unsigned int x, unsigned int y)
|
||||
return 0;
|
||||
}
|
||||
unsigned int ret = SrchMap[y*Width+x];
|
||||
if (ret&(PATH_MAP_DOOR_TRANSPARENT|PATH_MAP_ACTOR)) {
|
||||
if (ret&(PATH_MAP_DOOR_IMPASSABLE|PATH_MAP_ACTOR)) {
|
||||
ret&=~PATH_MAP_PASSABLE;
|
||||
}
|
||||
if (ret&PATH_MAP_DOOR_OPAQUE) {
|
||||
@@ -2083,7 +2120,18 @@ bool Map::CanFree()
|
||||
|
||||
void Map::DebugDump(bool show_actors) const
|
||||
{
|
||||
size_t i;
|
||||
|
||||
printf( "DebugDump of Area %s:\n", scriptName );
|
||||
printf ("Scripts:");
|
||||
|
||||
for (i = 0; i < MAX_SCRIPTS; i++) {
|
||||
const char* poi = "<none>";
|
||||
if (Scripts[i]) {
|
||||
poi = Scripts[i]->GetName();
|
||||
}
|
||||
printf( " %.8s", poi );
|
||||
}
|
||||
printf( "Area Global ID: %d\n", GetGlobalID());
|
||||
printf( "OutDoor: %s\n", YESNO(AreaType & AT_OUTDOOR ) );
|
||||
printf( "Day/Night: %s\n", YESNO(AreaType & AT_DAYNIGHT ) );
|
||||
@@ -2094,7 +2142,7 @@ void Map::DebugDump(bool show_actors) const
|
||||
|
||||
if (show_actors) {
|
||||
printf("\n");
|
||||
size_t i = actors.size();
|
||||
i = actors.size();
|
||||
while (i--) {
|
||||
if (!(actors[i]->GetInternalFlag()&(IF_JUSTDIED|IF_REALLYDIED))) {
|
||||
printf("Actor: %s at %d.%d\n", actors[i]->GetName(1), actors[i]->Pos.x, actors[i]->Pos.y);
|
||||
@@ -2981,7 +3029,7 @@ void Map::SetMapVisibility(int setreset)
|
||||
memset( VisibleBitmap, setreset, GetExploredMapSize() );
|
||||
}
|
||||
|
||||
// x, y are in tile coordinates
|
||||
// x, y are not in tile coordinates
|
||||
void Map::ExploreTile(const Point &pos)
|
||||
{
|
||||
int h = TMap->YCellCount * 2 + LargeFog;
|
||||
@@ -3302,10 +3350,14 @@ void Map::FadeSparkle(const Point &pos, bool forced)
|
||||
}
|
||||
}
|
||||
|
||||
void Map::Sparkle(ieDword duration, ieDword color, ieDword type, const Point &pos, unsigned int FragAnimID)
|
||||
void Map::Sparkle(ieDword duration, ieDword color, ieDword type, const Point &pos, unsigned int FragAnimID, int Zpos)
|
||||
{
|
||||
int style, path, grow, size, width, ttl;
|
||||
|
||||
if (!Zpos) {
|
||||
Zpos = 30;
|
||||
}
|
||||
|
||||
//the high word is ignored in the original engine (compatibility hack)
|
||||
switch(type&0xffff) {
|
||||
case SPARKLE_SHOWER: //simple falling sparks
|
||||
@@ -3320,14 +3372,14 @@ void Map::Sparkle(ieDword duration, ieDword color, ieDword type, const Point &po
|
||||
grow = SP_SPAWN_SOME;
|
||||
size = 40;
|
||||
width = 40;
|
||||
ttl = core->GetGame()->GameTime+25;
|
||||
ttl = core->GetGame()->GameTime+Zpos;
|
||||
break;
|
||||
case SPARKLE_EXPLOSION: //this isn't in the original engine, but it is a nice effect to have
|
||||
path = SP_PATH_EXPL;
|
||||
grow = SP_SPAWN_SOME;
|
||||
size = 40;
|
||||
size = 10;
|
||||
width = 40;
|
||||
ttl = core->GetGame()->GameTime+25;
|
||||
ttl = core->GetGame()->GameTime+Zpos;
|
||||
break;
|
||||
default:
|
||||
path = SP_PATH_FLIT;
|
||||
@@ -3339,7 +3391,7 @@ void Map::Sparkle(ieDword duration, ieDword color, ieDword type, const Point &po
|
||||
}
|
||||
Particles *sparkles = new Particles(size);
|
||||
sparkles->SetOwner(this);
|
||||
sparkles->SetRegion(pos.x-width/2, pos.y-30, width, 30);
|
||||
sparkles->SetRegion(pos.x-width/2, pos.y-Zpos, width, Zpos);
|
||||
sparkles->SetTimeToLive(ttl);
|
||||
|
||||
if (FragAnimID) {
|
||||
@@ -3528,7 +3580,7 @@ void AreaAnimation::BlendAnimation()
|
||||
palette->CreateShadedAlphaChannel();
|
||||
}
|
||||
|
||||
bool AreaAnimation::Schedule(ieDword gametime)
|
||||
bool AreaAnimation::Schedule(ieDword gametime) const
|
||||
{
|
||||
if (!(Flags&A_ANI_ACTIVE) ) {
|
||||
return false;
|
||||
@@ -3542,9 +3594,17 @@ bool AreaAnimation::Schedule(ieDword gametime)
|
||||
return false;
|
||||
}
|
||||
|
||||
int AreaAnimation::GetHeight() const
|
||||
{
|
||||
if (Flags&A_ANI_BACKGROUND) return -9999;
|
||||
return Pos.y+height;
|
||||
//FIXME: this is obviously a hack that is destined to crash, also useless, so
|
||||
//See ar9101 in HoW and ar0602 in bg2 before committing anything
|
||||
//return Pos.y+height-animation[0][0].GetFrame(0)->Height;
|
||||
}
|
||||
|
||||
void AreaAnimation::Draw(const Region &screen, Map *area)
|
||||
{
|
||||
int ac=animcount;
|
||||
Video* video = core->GetVideoDriver();
|
||||
|
||||
//always draw the animation tinted because tint is also used for
|
||||
@@ -3559,7 +3619,8 @@ void AreaAnimation::Draw(const Region &screen, Map *area)
|
||||
covers=(SpriteCover **) calloc( animcount, sizeof(SpriteCover *) );
|
||||
}
|
||||
}
|
||||
ac=animcount;
|
||||
|
||||
int ac = animcount;
|
||||
while (ac--) {
|
||||
Animation *anim = animation[ac];
|
||||
Sprite2D *frame = anim->NextFrame();
|
||||
@@ -3629,3 +3690,15 @@ void Map::SetInternalSearchMap(int x, int y, int value) {
|
||||
}
|
||||
SrchMap[x+y*Width] = value;
|
||||
}
|
||||
|
||||
void Map::SetBackground(const ieResRef &bgResRef, ieDword duration) {
|
||||
Video* video = core->GetVideoDriver();
|
||||
|
||||
ResourceHolder<ImageMgr> bmp(bgResRef);
|
||||
|
||||
if (Background) {
|
||||
video->FreeSprite(Background);
|
||||
}
|
||||
Background = bmp->GetSprite2D();
|
||||
BgDuration = duration;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Map;
|
||||
#include "Image.h"
|
||||
#include "IniSpawn.h"
|
||||
#include "SpriteCover.h"
|
||||
#include "Scriptable/ActorBlock.h"
|
||||
#include "Scriptable/Scriptable.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
@@ -153,8 +153,8 @@ public:
|
||||
|
||||
class TerrainSounds {
|
||||
public:
|
||||
ieResRef Group;
|
||||
ieResRef Sounds[16];
|
||||
ieResRef Group;
|
||||
ieResRef Sounds[16];
|
||||
};
|
||||
|
||||
class SpawnGroup {
|
||||
@@ -204,8 +204,9 @@ public:
|
||||
void InitAnimation();
|
||||
void SetPalette(ieResRef PaletteRef);
|
||||
void BlendAnimation();
|
||||
bool Schedule(ieDword gametime);
|
||||
bool Schedule(ieDword gametime) const;
|
||||
void Draw(const Region &screen, Map *area);
|
||||
int GetHeight() const;
|
||||
private:
|
||||
Animation *GetAnimationPiece(AnimationFactory *af, int animCycle);
|
||||
};
|
||||
@@ -244,12 +245,15 @@ public:
|
||||
bool DayNight;
|
||||
//movies for day/night (only in ToB)
|
||||
ieResRef Dream[2];
|
||||
Sprite2D *Background;
|
||||
ieDword BgDuration;
|
||||
|
||||
private:
|
||||
ieStrRef trackString;
|
||||
int trackFlag;
|
||||
ieWord trackDiff;
|
||||
unsigned short* MapSet;
|
||||
unsigned short* SrchMap; //internal searchmap
|
||||
unsigned short* SrchMap; //internal searchmap
|
||||
std::queue< unsigned int> InternalStack;
|
||||
unsigned int Width, Height;
|
||||
std::list< AreaAnimation*> animations;
|
||||
@@ -373,7 +377,7 @@ public:
|
||||
//adds a sparkle puff of colour to a point in the area
|
||||
//FragAnimID is an optional avatar animation ID (see avatars.2da) for
|
||||
//fragment animation
|
||||
void Sparkle(ieDword duration, ieDword color, ieDword type, const Point &pos, unsigned int FragAnimID = 0);
|
||||
void Sparkle(ieDword duration, ieDword color, ieDword type, const Point &pos, unsigned int FragAnimID = 0, int Zpos = 0);
|
||||
//removes or fades the sparkle puff at a point
|
||||
void FadeSparkle(const Point &pos, bool forced);
|
||||
|
||||
@@ -470,6 +474,7 @@ public:
|
||||
unsigned int GetLightLevel(const Point &Pos);
|
||||
unsigned short GetInternalSearchMap(int x, int y);
|
||||
void SetInternalSearchMap(int x, int y, int value);
|
||||
void SetBackground(const ieResRef &bgResref, ieDword duration);
|
||||
private:
|
||||
AreaAnimation *GetNextAreaAnimation(aniIterator &iter, ieDword gametime);
|
||||
Particles *GetNextSpark(spaIterator &iter);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "Map.h"
|
||||
#include "Plugin.h"
|
||||
#include "Scriptable/ActorBlock.h"
|
||||
#include "Scriptable/Scriptable.h"
|
||||
#include "System/DataStream.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,8 @@ public:
|
||||
Color col[256]; //< RGB or RGBA 8 bit palette
|
||||
bool alpha; //< true if this is a RGBA palette
|
||||
bool named; //< true if the palette comes from a bmp and cached
|
||||
Color front; // Original colors used by core->CreatePalette()
|
||||
Color back;
|
||||
|
||||
void IncRef() {
|
||||
refcount++;
|
||||
|
||||
@@ -331,7 +331,7 @@ int Particles::Update()
|
||||
continue;
|
||||
}
|
||||
drawn=true;
|
||||
if (!points[i].state) {
|
||||
if (!points[i].state) {
|
||||
grow++;
|
||||
}
|
||||
points[i].state--;
|
||||
|
||||
@@ -31,11 +31,11 @@ enum {
|
||||
PATH_MAP_AREAMASK = 15,
|
||||
PATH_MAP_FREE = 0,
|
||||
PATH_MAP_DOOR_OPAQUE = 16,
|
||||
PATH_MAP_DOOR_TRANSPARENT = 32,
|
||||
PATH_MAP_DOOR_IMPASSABLE = 32,
|
||||
PATH_MAP_PC = 64,
|
||||
PATH_MAP_NPC = 128,
|
||||
PATH_MAP_ACTOR = (PATH_MAP_PC|PATH_MAP_NPC),
|
||||
PATH_MAP_DOOR = (PATH_MAP_DOOR_OPAQUE|PATH_MAP_DOOR_TRANSPARENT),
|
||||
PATH_MAP_DOOR = (PATH_MAP_DOOR_OPAQUE|PATH_MAP_DOOR_IMPASSABLE),
|
||||
PATH_MAP_NOTAREA = (PATH_MAP_ACTOR|PATH_MAP_DOOR),
|
||||
PATH_MAP_NOTDOOR = (PATH_MAP_ACTOR|PATH_MAP_AREAMASK),
|
||||
PATH_MAP_NOTACTOR = (PATH_MAP_DOOR|PATH_MAP_AREAMASK)
|
||||
|
||||
@@ -34,11 +34,14 @@
|
||||
#include <cstdlib>
|
||||
|
||||
//to get gradient color
|
||||
#define PALSIZE 12
|
||||
//apparently pst doesn't have the small palette correctly
|
||||
#define PALSIZE 32
|
||||
|
||||
static const ieByte SixteenToNine[MAX_ORIENT]={0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1};
|
||||
static const ieByte SixteenToFive[MAX_ORIENT]={0,1,2,3,4,3,2,1,0,1,2,3,4,3,2,1};
|
||||
|
||||
static ProjectileServer *server = NULL;
|
||||
|
||||
Projectile::Projectile()
|
||||
{
|
||||
autofree = false;
|
||||
@@ -62,6 +65,13 @@ Projectile::Projectile()
|
||||
memset(smokebam, 0, sizeof(smokebam));
|
||||
light = NULL;
|
||||
pathcounter = 0x7fff;
|
||||
FakeTarget = 0;
|
||||
bend = 0;
|
||||
drawSpark = 0;
|
||||
ZPos = 0;
|
||||
extension_delay = 0;
|
||||
if (!server)
|
||||
server = core->GetProjectileServer();
|
||||
}
|
||||
|
||||
Projectile::~Projectile()
|
||||
@@ -77,8 +87,8 @@ Projectile::~Projectile()
|
||||
ClearPath();
|
||||
|
||||
if (travel_handle) {
|
||||
travel_handle->Stop();
|
||||
travel_handle.release();
|
||||
//allow an explosion sound to finish completely
|
||||
travel_handle->StopLooping();
|
||||
}
|
||||
|
||||
if (phase != P_UNINITED) {
|
||||
@@ -253,10 +263,13 @@ void Projectile::SetBlend()
|
||||
//create another projectile with type-1 (iterate magic missiles and call lightning)
|
||||
void Projectile::CreateIteration()
|
||||
{
|
||||
ProjectileServer *server = core->GetProjectileServer();
|
||||
Projectile *pro = server->GetProjectileByIndex(type-1);
|
||||
pro->SetEffectsCopy(effects);
|
||||
pro->SetCaster(Caster);
|
||||
pro->SetCaster(Caster, Level);
|
||||
if (ExtFlags&PEF_CURVE) {
|
||||
pro->bend=bend+1;
|
||||
}
|
||||
|
||||
if (FakeTarget) {
|
||||
area->AddProjectile(pro, Pos, FakeTarget, true);
|
||||
} else {
|
||||
@@ -302,15 +315,19 @@ void Projectile::Setup()
|
||||
}
|
||||
}
|
||||
|
||||
if(Shake) {
|
||||
core->timer->SetScreenShake( Shake, Shake, Shake);
|
||||
}
|
||||
|
||||
//falling = vertical
|
||||
//incoming = right side
|
||||
//both = left side
|
||||
if(ExtFlags&(PEF_FALLING|PEF_INCOMING) ) {
|
||||
if (ExtFlags&PEF_FALLING) {
|
||||
if (ExtFlags&PEF_INCOMING) {
|
||||
if (ExtFlags&PEF_FALLING) {
|
||||
Pos.x=Destination.x-200;
|
||||
} else {
|
||||
Pos.x=Destination.x+200;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Pos.x=Destination.x;
|
||||
} else {
|
||||
Pos.x=Destination.x+200;
|
||||
}
|
||||
Pos.y=Destination.y-200;
|
||||
NextTarget(Destination);
|
||||
@@ -324,6 +341,7 @@ void Projectile::Setup()
|
||||
//but also makes the caster immune to the effect
|
||||
if (Extension) {
|
||||
if (Extension->AFlags&PAF_CONE) {
|
||||
NewOrientation = Orientation = GetOrient(Destination, Pos);
|
||||
Destination=Pos;
|
||||
ExtFlags|=PEF_NO_TRAVEL;
|
||||
}
|
||||
@@ -366,13 +384,16 @@ void Projectile::Setup()
|
||||
if(ExtFlags&PEF_POP) {
|
||||
//the explosion consists of a pop in/hold/pop out of the travel projectile (dimension door)
|
||||
if(travel[0] && shadow[0]) {
|
||||
SetDelay(travel[0]->GetFrameCount()*2+shadow[0]->GetFrameCount() );
|
||||
extension_delay = travel[0]->GetFrameCount()*2+shadow[0]->GetFrameCount();
|
||||
//SetDelay( travel[0]->GetFrameCount()*2+shadow[0]->GetFrameCount());
|
||||
travel[0]->Flags|=A_ANI_PLAYONCE;
|
||||
shadow[0]->Flags|=A_ANI_PLAYONCE;
|
||||
}
|
||||
} else {
|
||||
if(travel[0]) {
|
||||
SetDelay(travel[0]->GetFrameCount() );
|
||||
extension_delay = travel[0]->GetFrameCount();
|
||||
travel[0]->Flags|=A_ANI_PLAYONCE;
|
||||
//SetDelay(travel[0]->GetFrameCount() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,6 +411,9 @@ void Projectile::Setup()
|
||||
if (TFlags&PTF_BLEND) {
|
||||
SetBlend();
|
||||
}
|
||||
if (SFlags&PSF_FLYING) {
|
||||
ZPos = FLY_HEIGHT;
|
||||
}
|
||||
phase = P_TRAVEL;
|
||||
travel_handle = core->GetAudioDrv()->Play(SoundRes1, Pos.x, Pos.y, (SFlags & PSF_LOOPING ? GEM_SND_LOOPING : 0));
|
||||
|
||||
@@ -436,11 +460,79 @@ void Projectile::SetDelay(int delay)
|
||||
ExtFlags|=PEF_FREEZE;
|
||||
}
|
||||
|
||||
//copied from Actor.cpp
|
||||
#define ATTACKROLL 20
|
||||
#define WEAPON_FIST 0
|
||||
|
||||
bool Projectile::FailedIDS(Actor *target) const
|
||||
{
|
||||
bool fail = !EffectQueue::match_ids( target, IDSType, IDSValue);
|
||||
if (ExtFlags&PEF_NOTIDS) {
|
||||
fail = !fail;
|
||||
}
|
||||
if (ExtFlags&PEF_BOTH) {
|
||||
if (!fail) {
|
||||
fail = !EffectQueue::match_ids( target, IDSType2, IDSValue2);
|
||||
if (ExtFlags&PEF_NOTIDS2) {
|
||||
fail = !fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fail && IDSType2) {
|
||||
fail = !EffectQueue::match_ids( target, IDSType2, IDSValue2);
|
||||
if (ExtFlags&PEF_NOTIDS2) {
|
||||
fail = !fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fail) {
|
||||
if(ExtFlags&PEF_TOUCH) {
|
||||
Actor *caster = core->GetGame()->GetActorByGlobalID(Caster);
|
||||
if (caster) {
|
||||
//TODO move this to Actor
|
||||
//TODO some projectiles use melee attack (fist), others use projectile attack
|
||||
//this apparently depends on the spell's spell form (normal vs. projectile)
|
||||
int roll = caster->LuckyRoll(1, ATTACKROLL, 0);
|
||||
if (roll==1) {
|
||||
return true; //critical failure
|
||||
}
|
||||
|
||||
if (!(target->GetStat(IE_STATE_ID)&STATE_CRIT_PROT)) {
|
||||
if (roll >= (ATTACKROLL - (int) caster->GetStat(IE_CRITICALHITBONUS))) {
|
||||
return false; //critical success
|
||||
}
|
||||
}
|
||||
|
||||
//handle attack type here, weapon depends on it too?
|
||||
int tohit = caster->GetToHit(0, WEAPON_FIST, target);
|
||||
//damage type, should be generic?
|
||||
int defense = target->GetDefense(0, caster);
|
||||
if(target->IsReverseToHit()) {
|
||||
fail = roll + defense < tohit;
|
||||
} else {
|
||||
fail = tohit + roll < defense;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
void Projectile::Payload()
|
||||
{
|
||||
Actor *target;
|
||||
|
||||
if(!effects) {
|
||||
if(Shake) {
|
||||
core->timer->SetScreenShake( Shake, Shake, Shake);
|
||||
Shake = 0;
|
||||
}
|
||||
|
||||
//allow area affecting projectile with a spell
|
||||
if(!(effects || (!Target && FailSpell[0]))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -459,28 +551,77 @@ void Projectile::Payload()
|
||||
target = core->GetGame()->GetActorByGlobalID(FakeTarget);
|
||||
}
|
||||
} else {
|
||||
target = area->GetActorByGlobalID(Caster);
|
||||
|
||||
target = area->GetActorByGlobalID(Caster);
|
||||
}
|
||||
Actor *source = area->GetActorByGlobalID(Caster);
|
||||
if (source) {
|
||||
effects->SetOwner(source);
|
||||
} else {
|
||||
effects->SetOwner(target);
|
||||
if (effects) {
|
||||
if (source) {
|
||||
effects->SetOwner(source);
|
||||
} else {
|
||||
effects->SetOwner(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
if(ExtFlags&PEF_RGB) {
|
||||
target->SetColorMod(0xff, RGBModifier::ADD, ColorSpeed,
|
||||
RGB >> 8, RGB >> 16, RGB >> 24);
|
||||
}
|
||||
|
||||
effects->AddAllEffects(target, Destination);
|
||||
if (target) {
|
||||
//apply this spell on target when the projectile fails
|
||||
if (FailedIDS(target)) {
|
||||
if (FailSpell[0]) {
|
||||
if (Target) {
|
||||
core->ApplySpell(FailSpell, target, effects->GetOwner(), Level);
|
||||
} else {
|
||||
//no Target, using the fake target as owner
|
||||
core->ApplySpellPoint(FailSpell, area, Destination, target, Level);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//apply this spell on the target when the projectile succeeds
|
||||
if (SuccSpell[0]) {
|
||||
core->ApplySpell(SuccSpell, target, effects->GetOwner(), Level);
|
||||
}
|
||||
|
||||
if(ExtFlags&PEF_RGB) {
|
||||
target->SetColorMod(0xff, RGBModifier::ADD, ColorSpeed,
|
||||
RGB >> 8, RGB >> 16, RGB >> 24);
|
||||
}
|
||||
|
||||
effects->AddAllEffects(target, Destination);
|
||||
}
|
||||
}
|
||||
|
||||
delete effects;
|
||||
effects = NULL;
|
||||
}
|
||||
|
||||
void Projectile::ApplyDefault()
|
||||
{
|
||||
Actor *actor = area->GetActorByGlobalID(Caster);
|
||||
if (actor) {
|
||||
//name is the projectile's name
|
||||
//for simplicity, we apply a spell of the same name
|
||||
core->ApplySpell(name, actor, actor, Level);
|
||||
}
|
||||
}
|
||||
|
||||
void Projectile::StopSound()
|
||||
{
|
||||
if (travel_handle) {
|
||||
travel_handle->Stop();
|
||||
travel_handle.release();
|
||||
}
|
||||
}
|
||||
|
||||
void Projectile::UpdateSound()
|
||||
{
|
||||
if (!(SFlags&PSF_SOUND2)) {
|
||||
StopSound();
|
||||
}
|
||||
if (!travel_handle || !travel_handle->Playing()) {
|
||||
travel_handle = core->GetAudioDrv()->Play(SoundRes2, Pos.x, Pos.y, (SFlags & PSF_LOOPING2 ? GEM_SND_LOOPING : 0));
|
||||
SFlags|=PSF_SOUND2;
|
||||
}
|
||||
}
|
||||
|
||||
//control the phase change when the projectile reached its target
|
||||
//possible actions: vanish, hover over point, explode
|
||||
//depends on the area extension
|
||||
@@ -494,25 +635,46 @@ void Projectile::ChangePhase()
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (phase == P_TRAVEL) {
|
||||
if ((ExtFlags&PEF_DELAY) && extension_delay) {
|
||||
extension_delay--;
|
||||
UpdateSound();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//reached target, and explodes now
|
||||
if (!Extension) {
|
||||
if (travel_handle) {
|
||||
travel_handle->Stop();
|
||||
travel_handle.release();
|
||||
}
|
||||
//there are no-effect projectiles, like missed arrows
|
||||
//Payload can redirect the projectile in case of projectile reflection
|
||||
Payload();
|
||||
if (phase ==P_TRAVEL) {
|
||||
if(ExtFlags&PEF_DEFSPELL) {
|
||||
ApplyDefault();
|
||||
}
|
||||
StopSound();
|
||||
Payload();
|
||||
phase = P_TRAVEL2;
|
||||
}
|
||||
//freeze on target, this is recommended only for child projectiles
|
||||
//as the projectile won't go away on its own
|
||||
if(ExtFlags&PEF_FREEZE) {
|
||||
if(extension_delay) {
|
||||
if (extension_delay>0) {
|
||||
extension_delay--;
|
||||
UpdateSound();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (phase == P_TRAVEL2) {
|
||||
if (extension_delay) {
|
||||
extension_delay--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(ExtFlags&PEF_FADE) {
|
||||
TFlags &= ~PTF_TINT; //turn off area tint
|
||||
tint.a--;
|
||||
@@ -520,9 +682,6 @@ void Projectile::ChangePhase()
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
phase = P_EXPIRED;
|
||||
return;
|
||||
}
|
||||
|
||||
EndTravel();
|
||||
@@ -553,6 +712,8 @@ int Projectile::CalculateExplosionCount()
|
||||
|
||||
void Projectile::EndTravel()
|
||||
{
|
||||
StopSound();
|
||||
UpdateSound();
|
||||
if(!Extension) {
|
||||
phase = P_EXPIRED;
|
||||
return;
|
||||
@@ -565,24 +726,23 @@ void Projectile::EndTravel()
|
||||
} else {
|
||||
phase = P_EXPLODING1;
|
||||
}
|
||||
|
||||
if (travel_handle) {
|
||||
travel_handle->Stop();
|
||||
travel_handle.release();
|
||||
}
|
||||
|
||||
//this sound is not played for projectiles waiting for trigger
|
||||
//it is probably also played when a travel projectile ends its mission
|
||||
core->GetAudioDrv()->Play(SoundRes2, Pos.x, Pos.y);
|
||||
}
|
||||
|
||||
void Projectile::AddTrail(ieResRef BAM, const ieByte *pal)
|
||||
int Projectile::AddTrail(ieResRef BAM, const ieByte *pal)
|
||||
{
|
||||
ScriptedAnimation *sca=gamedata->GetScriptedAnimation(BAM,0);
|
||||
if (!sca) return;
|
||||
if (!sca) return 0;
|
||||
if(pal) {
|
||||
for(int i=0;i<7;i++) {
|
||||
sca->SetPalette(pal[i], 4+i*PALSIZE);
|
||||
if (ExtFlags & PEF_TINT) {
|
||||
Color tmpColor[PALSIZE];
|
||||
|
||||
core->GetPalette( pal[0], PALSIZE, tmpColor );
|
||||
sca->Tint = tmpColor[PALSIZE/2];
|
||||
sca->Transparency |= BLIT_TINTED;
|
||||
} else {
|
||||
for(int i=0;i<7;i++) {
|
||||
sca->SetPalette(pal[i], 4+i*PALSIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
sca->SetOrientation(Orientation);
|
||||
@@ -591,6 +751,7 @@ void Projectile::AddTrail(ieResRef BAM, const ieByte *pal)
|
||||
sca->XPos += Pos.x;
|
||||
sca->YPos += Pos.y;
|
||||
area->AddVVCell(sca);
|
||||
return sca->GetSequenceDuration(AI_UPDATE_TIME);
|
||||
}
|
||||
|
||||
void Projectile::DoStep(unsigned int walk_speed)
|
||||
@@ -601,6 +762,15 @@ void Projectile::DoStep(unsigned int walk_speed)
|
||||
ClearPath();
|
||||
}
|
||||
|
||||
//intro trailing, drawn only once at the beginning
|
||||
if (pathcounter==0x7ffe) {
|
||||
for(int i=0;i<3;i++) {
|
||||
if(!TrailSpeed[i] && TrailBAM[i][0]) {
|
||||
extension_delay = AddTrail(TrailBAM[i], (ExtFlags&PEF_TINT)?Gradients:NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
ChangePhase();
|
||||
return;
|
||||
@@ -621,7 +791,7 @@ void Projectile::DoStep(unsigned int walk_speed)
|
||||
|
||||
for(int i=0;i<3;i++) {
|
||||
if(TrailSpeed[i] && !(pathcounter%TrailSpeed[i])) {
|
||||
AddTrail(TrailBAM[i], 0);
|
||||
AddTrail(TrailBAM[i], (ExtFlags&PEF_TINT)?Gradients:NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,6 +842,11 @@ void Projectile::DoStep(unsigned int walk_speed)
|
||||
if (!walk_speed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SFlags&PSF_SPARKS) {
|
||||
drawSpark = 1;
|
||||
}
|
||||
|
||||
if (step->Next->x > step->x)
|
||||
Pos.x += ( unsigned short )
|
||||
( ( step->Next->x - Pos.x ) * ( time - timeStartStep ) / walk_speed );
|
||||
@@ -684,11 +859,13 @@ void Projectile::DoStep(unsigned int walk_speed)
|
||||
else
|
||||
Pos.y -= ( unsigned short )
|
||||
( ( Pos.y - step->Next->y ) * ( time - timeStartStep ) / walk_speed );
|
||||
|
||||
}
|
||||
|
||||
void Projectile::SetCaster(ieDword caster)
|
||||
void Projectile::SetCaster(ieDword caster, int level)
|
||||
{
|
||||
Caster=caster;
|
||||
Level=level;
|
||||
}
|
||||
|
||||
ieDword Projectile::GetCaster() const
|
||||
@@ -785,6 +962,10 @@ int Projectile::CalculateTargetFlag()
|
||||
//if there are any, then change phase to exploding
|
||||
int flags = GA_NO_DEAD;
|
||||
|
||||
if (Extension->AFlags&PAF_NO_WALL) {
|
||||
flags|=GA_NO_LOS;
|
||||
}
|
||||
|
||||
//projectiles don't affect dead/inanimate normally
|
||||
if (Extension->AFlags&PAF_INANIMATE) {
|
||||
flags&=~GA_NO_DEAD;
|
||||
@@ -805,6 +986,11 @@ int Projectile::CalculateTargetFlag()
|
||||
return flags;
|
||||
}
|
||||
|
||||
//this is the only way to affect neutrals and enemies
|
||||
if (Extension->APFlags&APF_INVERT_TARGET) {
|
||||
flags^=(GA_NO_ALLY|GA_NO_ENEMY);
|
||||
}
|
||||
|
||||
Actor *caster = area->GetActorByGlobalID(Caster);
|
||||
if (caster && ((Actor *) caster)->GetStat(IE_EA)<EA_GOODCUTOFF) {
|
||||
return flags;
|
||||
@@ -884,7 +1070,8 @@ void Projectile::LineTarget()
|
||||
void Projectile::SecondaryTarget()
|
||||
{
|
||||
//fail will become true if the projectile utterly failed to find a target
|
||||
bool fail= !!(Extension->APFlags&APF_SPELLFAIL);
|
||||
//if the spell was already applied on explosion, ignore this
|
||||
bool fail= !!(Extension->APFlags&APF_SPELLFAIL) && !(ExtFlags&PEF_DEFSPELL);
|
||||
int mindeg = 0;
|
||||
int maxdeg = 0;
|
||||
|
||||
@@ -894,11 +1081,18 @@ void Projectile::SecondaryTarget()
|
||||
maxdeg=mindeg+Extension->ConeWidth;
|
||||
}
|
||||
|
||||
ProjectileServer *server = core->GetProjectileServer();
|
||||
int radius = Extension->ExplosionRadius;
|
||||
Actor **actors = area->GetAllActorsInRadius(Pos, CalculateTargetFlag(), radius);
|
||||
Actor **poi=actors;
|
||||
|
||||
if (Extension->DiceCount) {
|
||||
//precalculate the maximum affected target count in case of PAF_AFFECT_ONE
|
||||
extension_targetcount = core->Roll(Extension->DiceCount, Extension->DiceSize, 0);
|
||||
} else {
|
||||
//this is the default case (for original engine)
|
||||
extension_targetcount = 1;
|
||||
}
|
||||
|
||||
while(*poi) {
|
||||
ieDword Target = (*poi)->GetGlobalID();
|
||||
|
||||
@@ -908,6 +1102,12 @@ void Projectile::SecondaryTarget()
|
||||
continue;
|
||||
}
|
||||
|
||||
//IDS targeting for area projectiles
|
||||
if (FailedIDS(*poi)) {
|
||||
poi++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Extension->AFlags&PAF_CONE) {
|
||||
//cone never affects the caster
|
||||
if(Caster==Target) {
|
||||
@@ -938,19 +1138,37 @@ void Projectile::SecondaryTarget()
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Projectile *pro = server->GetProjectileByIndex(Extension->ExplProjIdx);
|
||||
pro->SetEffectsCopy(effects);
|
||||
pro->SetCaster(Caster);
|
||||
//copy the additional effects reference to the child projectile
|
||||
//but only when there is a spell to copy
|
||||
if (SuccSpell[0])
|
||||
memcpy(pro->SuccSpell, SuccSpell, sizeof(ieResRef) );
|
||||
pro->SetCaster(Caster, Level);
|
||||
//this is needed to apply the success spell on the center point
|
||||
pro->SetTarget(Pos);
|
||||
//TODO:actually some of the splash projectiles are a good example of faketarget
|
||||
//projectiles (that don't follow the target, but still hit)
|
||||
area->AddProjectile(pro, Pos, Target, false);
|
||||
|
||||
poi++;
|
||||
fail=false;
|
||||
|
||||
//we already got one target affected in the AOE, this flag says
|
||||
//that was enough
|
||||
//that was enough (the GemRB extension can repeat this a random time (x d y)
|
||||
if(Extension->AFlags&PAF_AFFECT_ONE) {
|
||||
break;
|
||||
if (extension_targetcount<=0) {
|
||||
break;
|
||||
}
|
||||
//if target counting is per HD and this target is an actor, use the xp level field
|
||||
//otherwise count it as one
|
||||
if ((Extension->APFlags&APF_COUNT_HD) && ((*poi)->Type==ST_ACTOR) ) {
|
||||
Actor *actor = (Actor *) *poi;
|
||||
extension_targetcount-= actor->GetXPLevel(true);
|
||||
} else {
|
||||
extension_targetcount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(actors);
|
||||
@@ -958,12 +1176,7 @@ void Projectile::SecondaryTarget()
|
||||
//In case of utter failure, apply a spell of the same name on the caster
|
||||
//this feature is used by SCHARGE, PRTL_OP and PRTL_CL in the HoW pack
|
||||
if(fail) {
|
||||
Actor *actor = area->GetActorByGlobalID(Caster);
|
||||
if (actor) {
|
||||
//name is the projectile's name
|
||||
//for simplicity, we apply a spell of the same name
|
||||
core->ApplySpell(name, actor, actor, 0);
|
||||
}
|
||||
ApplyDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,7 +1200,7 @@ int Projectile::Update()
|
||||
SetTarget(Target, false);
|
||||
}
|
||||
|
||||
if (phase == P_TRAVEL) {
|
||||
if (phase == P_TRAVEL || phase == P_TRAVEL2) {
|
||||
DoStep(Speed);
|
||||
}
|
||||
return 1;
|
||||
@@ -1002,14 +1215,19 @@ void Projectile::Draw(const Region &screen)
|
||||
//This extension flag is to enable the travel projectile at
|
||||
//trigger/explosion time
|
||||
if (Extension->AFlags&PAF_VISIBLE) {
|
||||
//if (!Extension || (Extension->AFlags&PAF_VISIBLE)) {
|
||||
DrawTravel(screen);
|
||||
}
|
||||
/*
|
||||
if (!Extension) {
|
||||
return;
|
||||
}*/
|
||||
CheckTrigger(Extension->TriggerRadius);
|
||||
if (phase == P_EXPLODING1 || phase == P_EXPLODING2) {
|
||||
DrawExplosion(screen);
|
||||
}
|
||||
break;
|
||||
case P_TRAVEL:
|
||||
case P_TRAVEL: case P_TRAVEL2:
|
||||
//There is no Extension for simple traveling projectiles!
|
||||
DrawTravel(screen);
|
||||
return;
|
||||
@@ -1049,6 +1267,22 @@ void Projectile::DrawExploded(const Region &screen)
|
||||
phase = P_EXPIRED;
|
||||
}
|
||||
|
||||
void Projectile::SpawnFragment(Point &dest)
|
||||
{
|
||||
Projectile *pro = server->GetProjectileByIndex(Extension->FragProjIdx);
|
||||
if (pro) {
|
||||
if (Extension->AFlags&PAF_SECONDARY) {
|
||||
pro->SetEffectsCopy(effects);
|
||||
}
|
||||
pro->SetCaster(Caster, Level);
|
||||
if (pro->ExtFlags&PEF_RANDOM) {
|
||||
dest.x+=core->Roll(1,Extension->TileX, -Extension->TileX/2);
|
||||
dest.y+=core->Roll(1,Extension->TileY, -Extension->TileY/2);
|
||||
}
|
||||
area->AddProjectile(pro, dest, dest);
|
||||
}
|
||||
}
|
||||
|
||||
void Projectile::DrawExplosion(const Region &screen)
|
||||
{
|
||||
//This seems to be a needless safeguard
|
||||
@@ -1057,11 +1291,7 @@ void Projectile::DrawExplosion(const Region &screen)
|
||||
return;
|
||||
}
|
||||
|
||||
if (travel_handle) {
|
||||
travel_handle->Stop();
|
||||
travel_handle.release();
|
||||
}
|
||||
|
||||
StopSound();
|
||||
DrawChildren(screen);
|
||||
|
||||
int pause = core->IsFreezed();
|
||||
@@ -1088,6 +1318,9 @@ void Projectile::DrawExplosion(const Region &screen)
|
||||
LineTarget();
|
||||
}
|
||||
|
||||
int apflags = Extension->APFlags;
|
||||
int aoeflags = Extension->AFlags;
|
||||
|
||||
//no idea what is PAF_SECONDARY
|
||||
//probably it is to alter some behaviour in the secondary
|
||||
//projectile generation
|
||||
@@ -1098,37 +1331,66 @@ void Projectile::DrawExplosion(const Region &screen)
|
||||
SecondaryTarget();
|
||||
|
||||
//draw fragment graphics animation at the explosion center
|
||||
if (Extension->AFlags&PAF_FRAGMENT) {
|
||||
if (aoeflags&PAF_FRAGMENT) {
|
||||
//there is a character animation in the center of the explosion
|
||||
//which will go towards the edges (flames, etc)
|
||||
//Extension->ExplColor fake color for single shades (blue,green,red flames)
|
||||
//Extension->FragAnimID the animation id for the character animation
|
||||
//This color is not used in the original game
|
||||
area->Sparkle(0, Extension->ExplColor, SPARKLE_EXPLOSION, Pos, Extension->FragAnimID);
|
||||
Point pos = Pos;
|
||||
pos.x+=screen.x;
|
||||
pos.y+=screen.y;
|
||||
area->Sparkle(0, Extension->ExplColor, SPARKLE_EXPLOSION, pos, Extension->FragAnimID, GetZPos());
|
||||
}
|
||||
|
||||
if(Shake) {
|
||||
core->timer->SetScreenShake( Shake, Shake, Shake);
|
||||
Shake = 0;
|
||||
}
|
||||
|
||||
ProjectileServer *server = core->GetProjectileServer();
|
||||
//the center of the explosion could be another projectile played over the target
|
||||
//warning: this projectile doesn't inherit any effects, so its payload function
|
||||
//won't be doing anything (any effect of PAF_SECONDARY?)
|
||||
|
||||
if (Extension->FragProjIdx) {
|
||||
Projectile *pro = server->GetProjectileByIndex(Extension->FragProjIdx);
|
||||
if (pro) {
|
||||
area->AddProjectile(pro, Pos, Pos);
|
||||
if (apflags&APF_TILED) {
|
||||
int i,j;
|
||||
int radius = Extension->ExplosionRadius;
|
||||
|
||||
for (i=-radius;i<radius;i+=Extension->TileX) {
|
||||
for(j=-radius;j<radius;j+=Extension->TileY) {
|
||||
if (i*i+j*j<radius*radius) {
|
||||
Point p(Pos.x+i, Pos.y+j);
|
||||
SpawnFragment(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SpawnFragment(Pos);
|
||||
}
|
||||
}
|
||||
|
||||
//the center of the explosion is based on hardcoded explosion type (this is fireball.cpp in the original engine)
|
||||
//these resources are listed in areapro.2da and served by ProjectileServer.cpp
|
||||
int apflags = Extension->APFlags;
|
||||
|
||||
//draw it only once, at the time of explosion
|
||||
if (phase==P_EXPLODING1) {
|
||||
core->GetAudioDrv()->Play(Extension->SoundRes, Pos.x, Pos.y);
|
||||
//play VVC in center
|
||||
if (Extension->AFlags&PAF_VVC) {
|
||||
if (aoeflags&PAF_VVC) {
|
||||
ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(Extension->VVCRes, false);
|
||||
if (vvc) {
|
||||
if (apflags & APF_VVCPAL) {
|
||||
vvc->SetPalette(Extension->ExplColor);
|
||||
//if the palette is used as tint (as opposed to clown colorset) tint the vvc
|
||||
if (apflags & APF_TINT) {
|
||||
Color tmpColor[PALSIZE];
|
||||
|
||||
core->GetPalette( Extension->ExplColor, PALSIZE, tmpColor );
|
||||
vvc->Tint = tmpColor[PALSIZE/2];
|
||||
vvc->Transparency |= BLIT_TINTED;
|
||||
} else {
|
||||
vvc->SetPalette(Extension->ExplColor);
|
||||
}
|
||||
}
|
||||
//if the trail oriented, then the center is oriented too
|
||||
if (ExtFlags&PEF_TRAIL) {
|
||||
@@ -1163,7 +1425,7 @@ void Projectile::DrawExplosion(const Region &screen)
|
||||
}
|
||||
|
||||
//zero cone width means single line area of effect
|
||||
if((Extension->AFlags&PAF_CONE) && !Extension->ConeWidth) {
|
||||
if((aoeflags&PAF_CONE) && !Extension->ConeWidth) {
|
||||
child_size = 1;
|
||||
}
|
||||
|
||||
@@ -1197,7 +1459,7 @@ void Projectile::DrawExplosion(const Region &screen)
|
||||
}
|
||||
int max = 360;
|
||||
int add = 0;
|
||||
if (Extension->AFlags&PAF_CONE) {
|
||||
if (aoeflags&PAF_CONE) {
|
||||
max=Extension->ConeWidth;
|
||||
add=(Orientation*45-max)/2;
|
||||
}
|
||||
@@ -1266,7 +1528,7 @@ void Projectile::DrawExplosion(const Region &screen)
|
||||
}
|
||||
}
|
||||
|
||||
int Projectile::GetTravelPos(int face)
|
||||
int Projectile::GetTravelPos(int face) const
|
||||
{
|
||||
if (travel[face]) {
|
||||
return travel[face]->GetCurrentFrame();
|
||||
@@ -1274,7 +1536,7 @@ int Projectile::GetTravelPos(int face)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Projectile::GetShadowPos(int face)
|
||||
int Projectile::GetShadowPos(int face) const
|
||||
{
|
||||
if (shadow[face]) {
|
||||
return shadow[face]->GetCurrentFrame();
|
||||
@@ -1308,7 +1570,7 @@ void Projectile::SetupWall()
|
||||
|
||||
void Projectile::DrawLine(const Region &screen, int face, ieDword flag)
|
||||
{
|
||||
Video *video = core->GetVideoDriver();
|
||||
Video *video = core->GetVideoDriver();
|
||||
PathNode *iter = path;
|
||||
Sprite2D *frame = travel[face]->NextFrame();
|
||||
while(iter) {
|
||||
@@ -1355,12 +1617,7 @@ void Projectile::DrawTravel(const Region &screen)
|
||||
pos.x+=screen.x;
|
||||
pos.y+=screen.y;
|
||||
|
||||
if(ExtFlags&PEF_CURVE && phase == P_TRAVEL && Origin != Destination && type >= 68 && type <= 77) {
|
||||
// TODO: we want projectile# (starting at 0) because the lower-level
|
||||
// casts should be near the centre, so this is hard-coding the
|
||||
// magic missile id..
|
||||
int id = type - 68;
|
||||
|
||||
if(bend && phase == P_TRAVEL && Origin != Destination) {
|
||||
double total_distance = Distance(Origin, Destination);
|
||||
double travelled_distance = Distance(Origin, Pos);
|
||||
|
||||
@@ -1371,14 +1628,12 @@ void Projectile::DrawTravel(const Region &screen)
|
||||
// input to sin(): 0 to pi gives us an arc
|
||||
double arc_angle = travelled * M_PI;
|
||||
|
||||
//printf("id %d, travelled %f, angle %f\n", id, travelled, arc_angle);
|
||||
|
||||
// calculate the distance between the arc and the current pos
|
||||
// (this could use travelled and a larger constant multiplier,
|
||||
// to make the arc size fixed rather than relative to the total
|
||||
// distance to travel)
|
||||
double length_of_normal = travelled_distance * sin(arc_angle) * 0.3 * ((id / 2) + 1);
|
||||
if (id % 2) length_of_normal = -length_of_normal;
|
||||
double length_of_normal = travelled_distance * sin(arc_angle) * 0.3 * ((bend / 2) + 1);
|
||||
if (bend % 2) length_of_normal = -length_of_normal;
|
||||
|
||||
// adjust the to-be-rendered point by that distance
|
||||
double x_vector = (Destination.x - Origin.x) / total_distance,
|
||||
@@ -1426,9 +1681,7 @@ void Projectile::DrawTravel(const Region &screen)
|
||||
video->BlitGameSprite( frame, pos.x, pos.y, flag, tint, NULL, palette, &screen);
|
||||
}
|
||||
|
||||
if (SFlags&PSF_FLYING) {
|
||||
pos.y-=FLY_HEIGHT;
|
||||
}
|
||||
pos.y-=GetZPos();
|
||||
|
||||
if (ExtFlags&PEF_PILLAR) {
|
||||
//draw all frames simultaneously on top of each other
|
||||
@@ -1446,9 +1699,16 @@ void Projectile::DrawTravel(const Region &screen)
|
||||
}
|
||||
}
|
||||
|
||||
if (SFlags&PSF_SPARKS) {
|
||||
area->Sparkle(0,SparkColor,SPARKLE_EXPLOSION,pos);
|
||||
if (drawSpark) {
|
||||
area->Sparkle(0,SparkColor, SPARKLE_EXPLOSION, pos, 0, GetZPos() );
|
||||
drawSpark = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int Projectile::GetZPos() const
|
||||
{
|
||||
return ZPos;
|
||||
}
|
||||
|
||||
void Projectile::SetIdentifiers(const char *resref, ieWord id)
|
||||
|
||||
@@ -45,17 +45,21 @@
|
||||
//projectile phases
|
||||
#define P_UNINITED -1
|
||||
#define P_TRAVEL 0 //projectile moves to target
|
||||
#define P_TRIGGER 1 //projectile hovers over target, waits for trigger
|
||||
#define P_EXPLODING1 2 //projectile explosion spreads
|
||||
#define P_EXPLODING2 3 //projectile explosion repeats
|
||||
#define P_EXPLODED 4 //projectile spread over area
|
||||
#define P_TRAVEL2 1 //projectile hit target
|
||||
#define P_TRIGGER 2 //projectile hovers over target, waits for trigger
|
||||
#define P_EXPLODING1 3 //projectile explosion spreads
|
||||
#define P_EXPLODING2 4 //projectile explosion repeats
|
||||
#define P_EXPLODED 5 //projectile spread over area
|
||||
#define P_EXPIRED 99 //projectile scheduled for removal (existing parts are still drawn)
|
||||
|
||||
//projectile spark flags
|
||||
#define PSF_SPARKS 1
|
||||
#define PSF_FLYING 2
|
||||
#define PSF_LOOPING 4 //looping sound
|
||||
#define PSF_LOOPING 4 //looping sound
|
||||
#define PSF_LOOPING2 8 //looping second sound
|
||||
#define PSF_IGNORE_CENTER 16
|
||||
//gemrb specific internal flag
|
||||
#define PSF_SOUND2 0x80000000//already started sound2
|
||||
|
||||
//projectile travel flags
|
||||
#define PTF_COLOUR 1 //fake colours
|
||||
@@ -68,6 +72,7 @@
|
||||
//projectile extended travel flags (gemrb specific)
|
||||
#define PEF_BOUNCE 1 //bounce from walls (lightning bolt)
|
||||
#define PEF_CONTINUE 2 //continue as a travel projectile after trigger (lightning bolt)
|
||||
//TODO: This can probably be replaced by an area projectile trigger (like skull trap, glyph)
|
||||
#define PEF_FREEZE 4 //stay around (ice dagger)
|
||||
#define PEF_NO_TRAVEL 8 //all instant projectiles (draw upon holy might, finger of death)
|
||||
#define PEF_TRAIL 16 //trail bams facing value uses the same field as the travel projectile (otherwise it defaults to 9) (shout in iwd)
|
||||
@@ -77,7 +82,7 @@
|
||||
#define PEF_HALFTRANS 256 //half-transparency (holy might)
|
||||
#define PEF_TINT 512 //use palette gradient as tint
|
||||
#define PEF_ITERATION 1024 //create another projectile of type-1 (magic missiles)
|
||||
#define PEF_TILED 2048 //tiled AOE (bg1 cone of cold/fire)
|
||||
#define PEF_DEFSPELL 2048 //always apply the default spell on the caster
|
||||
#define PEF_FALLING 4096 //projectile falls down vertically (cow)
|
||||
#define PEF_INCOMING 8192 //projectile falls in on trajectory (comet)
|
||||
#define PEF_LINE 16384 //solid line between source and target (agannazar's scorcher)
|
||||
@@ -85,11 +90,17 @@
|
||||
#define PEF_BACKGROUND 0x10000 //draw under target,overrides flying (dimension door)
|
||||
#define PEF_POP 0x20000 //draw travel bam, then shadow, then travel bam backwards
|
||||
#define PEF_UNPOP 0x40000 //draw shadow, then travel bam (this is an internal flag)
|
||||
#define PEF_FADE 0x80000 //gradually fade on spot if used with PEF_FREEZE (ice dagger)
|
||||
//TODO: The next flag is probably not needed, it is done by a separate area hit animation
|
||||
#define PEF_FADE 0x80000 //gradually fade on spot if used with PEF_FREEZE (ice dagger)
|
||||
#define PEF_TEXT 0x100000//display text during setup
|
||||
#define PEF_WANDERING 0x200000//random movement (no real path)
|
||||
#define PEF_CYCLE 0x400000//random cycle
|
||||
#define PEF_RGB 0x800000//rgb pulse on hit
|
||||
#define PEF_TOUCH 0x1000000//successful to hit roll needed
|
||||
#define PEF_NOTIDS 0x2000000//negate IDS check
|
||||
#define PEF_NOTIDS2 0x4000000//negate secondary IDS check
|
||||
#define PEF_BOTH 0x8000000//both IDS check must succeed
|
||||
#define PEF_DELAY 0x10000000//delay payload until travel projectile cycle ends
|
||||
|
||||
//projectile area flags
|
||||
#define PAF_VISIBLE 1 //the travel projectile is visible until explosion
|
||||
@@ -110,7 +121,6 @@
|
||||
#define PAF_DELAY 0x4000 //
|
||||
#define PAF_AFFECT_ONE 0x8000 //
|
||||
|
||||
|
||||
//area projectile flags (in areapro.2da)
|
||||
//this functionality was hardcoded in the original engine, so the bit flags are
|
||||
//completely arbitrary (i assign them as need arises)
|
||||
@@ -132,6 +142,14 @@
|
||||
#define APF_MORE 128
|
||||
//apply spell on caster if failed to find target
|
||||
#define APF_SPELLFAIL 256
|
||||
//multiple directions
|
||||
#define APF_MULTIDIR 512
|
||||
//target HD counting
|
||||
#define APF_COUNT_HD 1024
|
||||
//target flag enemy ally switched
|
||||
#define APF_INVERT_TARGET 2048
|
||||
//tiled AoE animation
|
||||
#define APF_TILED 4096
|
||||
|
||||
struct ProjectileExtension
|
||||
{
|
||||
@@ -153,6 +171,11 @@ struct ProjectileExtension
|
||||
ieResRef Spread; //areapro.2da first resref
|
||||
ieResRef Secondary; //areapro.2da third resref
|
||||
ieResRef AreaSound; //areapro.2da second sound resource
|
||||
//used for target or HD counting
|
||||
ieWord DiceCount;
|
||||
ieWord DiceSize;
|
||||
ieWord TileX;
|
||||
ieWord TileY;
|
||||
};
|
||||
|
||||
class GEM_EXPORT Projectile
|
||||
@@ -166,13 +189,19 @@ public:
|
||||
ieDword SFlags;
|
||||
ieResRef SoundRes1;
|
||||
ieResRef SoundRes2;
|
||||
ieResRef SoundRes3;
|
||||
ieResRef TravelVVC;
|
||||
ieDword SparkColor;
|
||||
ieDword ExtFlags;
|
||||
ieDword StrRef;
|
||||
ieDword RGB;
|
||||
ieWord ColorSpeed;
|
||||
ieWord Shake;
|
||||
ieWord IDSType;
|
||||
ieWord IDSValue;
|
||||
ieWord IDSType2;
|
||||
ieWord IDSValue2;
|
||||
ieResRef FailSpell;
|
||||
ieResRef SuccSpell;
|
||||
////// gap
|
||||
ieDword TFlags;
|
||||
ieResRef BAMRes1;
|
||||
@@ -204,9 +233,11 @@ protected:
|
||||
//similar to normal actors
|
||||
Map *area;
|
||||
Point Pos;
|
||||
int ZPos;
|
||||
Point Destination;
|
||||
Point Origin;
|
||||
ieDword Caster; //the globalID of the caster actor
|
||||
int Level; //the caster's level
|
||||
ieDword Target; //the globalID of target actor
|
||||
ieDword FakeTarget; //a globalID for target that isn't followed
|
||||
int phase;
|
||||
@@ -216,6 +247,7 @@ protected:
|
||||
//these come from the extension area
|
||||
int extension_delay;
|
||||
int extension_explosioncount;
|
||||
int extension_targetcount;
|
||||
Color tint;
|
||||
|
||||
//special (not using char animations)
|
||||
@@ -226,10 +258,13 @@ protected:
|
||||
Projectile **children;
|
||||
int child_size;
|
||||
int pathcounter;
|
||||
int bend;
|
||||
int drawSpark;
|
||||
Holder<SoundHandle> travel_handle;
|
||||
public:
|
||||
void SetCaster(ieDword t);
|
||||
void SetCaster(ieDword t, int level);
|
||||
ieDword GetCaster() const;
|
||||
bool FailedIDS(Actor *target) const;
|
||||
void SetTarget(ieDword t, bool fake);
|
||||
void SetTarget(const Point &p);
|
||||
bool PointInRadius(const Point &p) const;
|
||||
@@ -325,23 +360,37 @@ private:
|
||||
void GetPaletteCopy(Animation *anim[], Palette *&pal);
|
||||
void GetSmokeAnim();
|
||||
void SetBlend();
|
||||
//apply spells and effects on the target, only in single travel mode
|
||||
//area effect projectiles call a separate single travel projectile for each affected target
|
||||
void Payload();
|
||||
//if there is an extension, convert to exploding or wait for trigger
|
||||
void EndTravel();
|
||||
//apply default spell
|
||||
void ApplyDefault();
|
||||
//stops the current sound
|
||||
void StopSound();
|
||||
//kickstarts the secondary sound
|
||||
void UpdateSound();
|
||||
//reached end of single travel missile, explode or expire now
|
||||
void ChangePhase();
|
||||
void AddTrail(ieResRef BAM, const ieByte *pal);
|
||||
//drop a BAM or VVC on the trail path, return the length of the animation
|
||||
int AddTrail(ieResRef BAM, const ieByte *pal);
|
||||
void DoStep(unsigned int walk_speed);
|
||||
void LineTarget(); //line projectiles (walls, scorchers)
|
||||
void SecondaryTarget(); //area projectiles (circles, cones)
|
||||
void CheckTrigger(unsigned int radius);
|
||||
//calculate target and destination points for a firewall
|
||||
void SetupWall();
|
||||
void DrawLine(const Region &screen, int face, ieDword flag);
|
||||
void DrawTravel(const Region &screen);
|
||||
bool DrawChildren(const Region &screen);
|
||||
void DrawExplosion(const Region &screen);
|
||||
void SpawnFragment(Point &pos);
|
||||
void DrawExploded(const Region &screen);
|
||||
int GetTravelPos(int face);
|
||||
int GetShadowPos(int face);
|
||||
int GetTravelPos(int face) const;
|
||||
int GetShadowPos(int face) const;
|
||||
void SetPos(int face, int frame1, int frame2);
|
||||
inline int GetZPos() const;
|
||||
|
||||
//logic to resolve target when single projectile hit destination
|
||||
int CalculateTargetFlag();
|
||||
|
||||
@@ -35,13 +35,23 @@ ResourceManager::~ResourceManager()
|
||||
{
|
||||
}
|
||||
|
||||
bool ResourceManager::AddSource(const char *path, const char *description, PluginID type)
|
||||
bool ResourceManager::AddSource(const char *path, const char *description, PluginID type, int flags)
|
||||
{
|
||||
PluginHolder<ResourceSource> source(type);
|
||||
if (!source->Open(path, description)) {
|
||||
return false;
|
||||
}
|
||||
searchPath.push_back(source);
|
||||
|
||||
if (flags & RM_REPLACE_SAME_SOURCE) {
|
||||
for (size_t i = 0; i < searchPath.size(); i++) {
|
||||
if (!stricmp(description, searchPath[i]->GetDescription())) {
|
||||
searchPath[i] = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
searchPath.push_back(source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "ResourceSource.h"
|
||||
#endif
|
||||
|
||||
#define RM_REPLACE_SAME_SOURCE 1
|
||||
|
||||
class DataStream;
|
||||
class Resource;
|
||||
class ResourceSource;
|
||||
@@ -49,7 +51,7 @@ public:
|
||||
* @param[in] description Description of the source.
|
||||
* @param[in] type Plugin type used for source.
|
||||
**/
|
||||
bool AddSource(const char *path, const char *description, PluginID type);
|
||||
bool AddSource(const char *path, const char *description, PluginID type, int flags=0);
|
||||
|
||||
/** returns true if resource exists */
|
||||
bool Exists(const char *ResRef, SClass_ID type, bool silent=false) const;
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
virtual DataStream* GetResource(const char* resname, const ResourceDesc &type) = 0;
|
||||
const char *GetDescription() const { return description; }
|
||||
protected:
|
||||
const char *description;
|
||||
char *description;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
//This class represents the .cre (creature) files.
|
||||
//Any player or non-player character is a creature.
|
||||
//Actor is a scriptable object (Scriptable). See ActorBlock.cpp
|
||||
//Actor is a scriptable object (Scriptable). See Scriptable.cpp
|
||||
|
||||
#include "Scriptable/Actor.h"
|
||||
|
||||
@@ -115,6 +115,7 @@ struct ItemUseType {
|
||||
static ItemUseType *itemuse = NULL;
|
||||
static int usecount = -1;
|
||||
static bool pstflags = false;
|
||||
static bool nocreate = false;
|
||||
//used in many places, but different in engines
|
||||
static ieDword state_invisible = STATE_INVISIBLE;
|
||||
|
||||
@@ -1371,6 +1372,7 @@ static void InitActorTables()
|
||||
int i, j;
|
||||
|
||||
pstflags = core->HasFeature(GF_PST_STATE_FLAGS);
|
||||
nocreate = core->HasFeature(GF_NO_NEW_VARIABLES);
|
||||
if (pstflags) {
|
||||
state_invisible=STATE_PST_INVIS;
|
||||
} else {
|
||||
@@ -1919,20 +1921,20 @@ static void InitActorTables()
|
||||
}
|
||||
}
|
||||
|
||||
//initializing the skill->stats conversion table (used in iwd2)
|
||||
tm.load("skillsta");
|
||||
if (tm) {
|
||||
int rowcount = tm->GetRowCount();
|
||||
skillcount = rowcount;
|
||||
if (rowcount) {
|
||||
skillstats = (int *) malloc(rowcount * sizeof(int) );
|
||||
skillabils = (int *) malloc(rowcount * sizeof(int) );
|
||||
while(rowcount--) {
|
||||
skillstats[rowcount]=core->TranslateStat(tm->QueryField(rowcount,0));
|
||||
skillabils[rowcount]=core->TranslateStat(tm->QueryField(rowcount,1));
|
||||
}
|
||||
}
|
||||
}
|
||||
//initializing the skill->stats conversion table (used in iwd2)
|
||||
tm.load("skillsta");
|
||||
if (tm) {
|
||||
int rowcount = tm->GetRowCount();
|
||||
skillcount = rowcount;
|
||||
if (rowcount) {
|
||||
skillstats = (int *) malloc(rowcount * sizeof(int) );
|
||||
skillabils = (int *) malloc(rowcount * sizeof(int) );
|
||||
while(rowcount--) {
|
||||
skillstats[rowcount]=core->TranslateStat(tm->QueryField(rowcount,0));
|
||||
skillabils[rowcount]=core->TranslateStat(tm->QueryField(rowcount,1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::SetLockedPalette(const ieDword *gradients)
|
||||
@@ -2185,17 +2187,7 @@ void Actor::RefreshEffects(EffectQueue *fx)
|
||||
//put all special cleanup calls here
|
||||
CharAnimations* anims = GetAnims();
|
||||
if (anims) {
|
||||
if (!anims->GlobalColorMod.locked) {
|
||||
anims->GlobalColorMod.type = RGBModifier::NONE;
|
||||
anims->GlobalColorMod.speed = 0;
|
||||
}
|
||||
unsigned int location;
|
||||
for (location = 0; location < 32; ++location) {
|
||||
if (!anims->ColorMods[location].phase) {
|
||||
anims->ColorMods[location].type = RGBModifier::NONE;
|
||||
anims->ColorMods[location].speed = 0;
|
||||
}
|
||||
}
|
||||
anims->CheckColorMod();
|
||||
}
|
||||
spellbook.ClearBonus();
|
||||
/* these apply resrefs should be on a list as a trigger+resref */
|
||||
@@ -2660,7 +2652,7 @@ void Actor::DialogInterrupt()
|
||||
}
|
||||
}
|
||||
|
||||
static EffectRef fx_cure_sleep_ref={"Cure:Sleep",NULL,-1};
|
||||
static EffectRef fx_cure_sleep_ref = { "Cure:Sleep", -1 };
|
||||
|
||||
void Actor::GetHit()
|
||||
{
|
||||
@@ -2687,7 +2679,7 @@ bool Actor::HandleCastingStance(const ieResRef SpellResRef, bool deplete)
|
||||
return false;
|
||||
}
|
||||
|
||||
static EffectRef fx_sleep_ref={"State:Helpless", NULL, -1};
|
||||
static EffectRef fx_sleep_ref = { "State:Helpless", -1 };
|
||||
|
||||
//returns actual damage
|
||||
int Actor::Damage(int damage, int damagetype, Scriptable *hitter, int modtype)
|
||||
@@ -2983,7 +2975,7 @@ void Actor::DebugDump()
|
||||
printf( "\nArea: %.8s ", Area );
|
||||
printf( "Dialog: %.8s\n", Dialog );
|
||||
printf( "Global ID: %d PartySlot: %d\n", GetGlobalID(), InParty);
|
||||
printf( "Script name:%.32s\n", scriptName );
|
||||
printf( "Script name:%.32s Current action: %d\n", scriptName, CurrentAction ? CurrentAction->actionID : -1);
|
||||
printf( "TalkCount: %d ", TalkCount );
|
||||
printf( "Allegiance: %d current allegiance:%d\n", BaseStats[IE_EA], Modified[IE_EA] );
|
||||
printf( "Class: %d current class:%d\n", BaseStats[IE_CLASS], Modified[IE_CLASS] );
|
||||
@@ -3210,9 +3202,9 @@ int Actor::GetEncumbrance()
|
||||
}
|
||||
|
||||
//bg2 and iwd1
|
||||
EffectRef control_creature_ref = { "ControlCreature", NULL, -1};
|
||||
static EffectRef control_creature_ref = { "ControlCreature", -1 };
|
||||
//iwd2
|
||||
EffectRef control_undead_ref = { "ControlUndead2", NULL, -1};
|
||||
static EffectRef control_undead_ref = { "ControlUndead2", -1 };
|
||||
|
||||
//receive turning
|
||||
void Actor::Turn(Scriptable *cleric, ieDword turnlevel)
|
||||
@@ -3242,7 +3234,7 @@ void Actor::Turn(Scriptable *cleric, ieDword turnlevel)
|
||||
LastTurner = cleric->GetGlobalID();
|
||||
if (turnlevel >= level+TURN_DEATH_LVL_MOD) {
|
||||
if (gamedata->Exists("panic", IE_SPL_CLASS_ID)) {
|
||||
core->ApplySpell("panic", this, cleric, 0);
|
||||
core->ApplySpell("panic", this, cleric, level);
|
||||
} else {
|
||||
printf("Panic from turning!\n");
|
||||
Panic(cleric, PANIC_RUNAWAY);
|
||||
@@ -3305,18 +3297,18 @@ void Actor::Resurrect()
|
||||
ieDword value=0;
|
||||
|
||||
game->kaputz->Lookup(DeathVar, value);
|
||||
if (value) {
|
||||
if (value>0) {
|
||||
game->kaputz->SetAt(DeathVar, value-1);
|
||||
}
|
||||
}
|
||||
//clear effects?
|
||||
}
|
||||
|
||||
static EffectRef fx_cure_poisoned_state_ref={"Cure:Poison",NULL,-1};
|
||||
static EffectRef fx_cure_hold_state_ref={"Cure:Hold",NULL,-1};
|
||||
static EffectRef fx_cure_stun_state_ref={"Cure:Stun",NULL,-1};
|
||||
static EffectRef fx_remove_portrait_icon_ref={"Icon:Remove",NULL,-1};
|
||||
static EffectRef fx_unpause_caster_ref={"Cure:CasterHold",NULL,-1};
|
||||
static EffectRef fx_cure_poisoned_state_ref = { "Cure:Poison", -1 };
|
||||
static EffectRef fx_cure_hold_state_ref = { "Cure:Hold", -1 };
|
||||
static EffectRef fx_cure_stun_state_ref = { "Cure:Stun", -1 };
|
||||
static EffectRef fx_remove_portrait_icon_ref = { "Icon:Remove", -1 };
|
||||
static EffectRef fx_unpause_caster_ref = { "Cure:CasterHold", -1 };
|
||||
|
||||
void Actor::Die(Scriptable *killer)
|
||||
{
|
||||
@@ -3329,7 +3321,6 @@ void Actor::Die(Scriptable *killer)
|
||||
//Can't simply set Selected to false, game has its own little list
|
||||
Game *game = core->GetGame();
|
||||
game->SelectActor(this, false, SELECT_NORMAL);
|
||||
game->OutAttack(GetGlobalID());
|
||||
|
||||
displaymsg->DisplayConstantStringName(STR_DEATH, 0xffffff, this);
|
||||
DisplayStringCore(this, VB_DIE, DS_CONSOLE|DS_CONST );
|
||||
@@ -3432,18 +3423,22 @@ void Actor::Die(Scriptable *killer)
|
||||
|
||||
// death variables are updated at the moment of death
|
||||
if (KillVar[0]) {
|
||||
//don't use the raw killVar here
|
||||
if (core->HasFeature(GF_HAS_KAPUTZ) ) {
|
||||
game->kaputz->Lookup(KillVar, value);
|
||||
game->kaputz->SetAt(KillVar, value+1);
|
||||
if (AppearanceFlags&APP_DEATHTYPE) {
|
||||
snprintf(varname, 32, "KILL_%s", KillVar);
|
||||
game->kaputz->Lookup(varname, value);
|
||||
game->kaputz->SetAt(varname, value+1, nocreate);
|
||||
}
|
||||
} else {
|
||||
// iwd/iwd2 path *sets* this var, so i changed it, not sure about pst above
|
||||
game->locals->SetAt(KillVar, 1);
|
||||
game->locals->SetAt(KillVar, 1, nocreate);
|
||||
}
|
||||
}
|
||||
if (IncKillVar[0]) {
|
||||
value = 0;
|
||||
game->locals->Lookup(IncKillVar, value);
|
||||
game->locals->SetAt(IncKillVar, value + 1);
|
||||
game->locals->SetAt(IncKillVar, value + 1, nocreate);
|
||||
}
|
||||
|
||||
if (scriptName[0]) {
|
||||
@@ -3452,29 +3447,24 @@ void Actor::Die(Scriptable *killer)
|
||||
if (AppearanceFlags&APP_DEATHVAR) {
|
||||
snprintf(varname, 32, "%s_DEAD", scriptName);
|
||||
game->kaputz->Lookup(varname, value);
|
||||
game->kaputz->SetAt(varname, value+1);
|
||||
}
|
||||
if (AppearanceFlags&APP_DEATHTYPE) {
|
||||
snprintf(varname, 32, "KILL_%s", KillVar);
|
||||
game->kaputz->Lookup(varname, value);
|
||||
game->kaputz->SetAt(varname, value+1);
|
||||
game->kaputz->SetAt(varname, value+1, nocreate);
|
||||
}
|
||||
} else {
|
||||
snprintf(varname, 32, core->GetDeathVarFormat(), scriptName);
|
||||
game->locals->Lookup(varname, value);
|
||||
game->locals->SetAt(varname, value+1);
|
||||
game->locals->SetAt(varname, value+1, nocreate);
|
||||
}
|
||||
|
||||
if (SetDeathVar) {
|
||||
value = 0;
|
||||
snprintf(varname, 32, "%s_DEAD", scriptName);
|
||||
game->locals->Lookup(varname, value);
|
||||
game->locals->SetAt(varname, 1);
|
||||
game->locals->SetAt(varname, 1, nocreate);
|
||||
if (value) {
|
||||
snprintf(varname, 32, "%s_KILL_CNT", scriptName);
|
||||
value = 1;
|
||||
game->locals->Lookup(varname, value);
|
||||
game->locals->SetAt(varname, value + 1);
|
||||
game->locals->SetAt(varname, value + 1, nocreate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3490,7 +3480,7 @@ void Actor::Die(Scriptable *killer)
|
||||
// todo: should probably not set this for humans in iwd?
|
||||
snprintf(varname, 32, "KILL_%s_CNT", raceName);
|
||||
game->locals->Lookup(varname, value);
|
||||
game->locals->SetAt(varname, value+1);
|
||||
game->locals->SetAt(varname, value+1, nocreate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3501,7 +3491,7 @@ void Actor::Die(Scriptable *killer)
|
||||
if (AppearanceFlags&j) {
|
||||
ieDword value = 0;
|
||||
game->locals->Lookup(CounterNames[i], value);
|
||||
game->locals->SetAt(CounterNames[i], value+DeathCounters[i]);
|
||||
game->locals->SetAt(CounterNames[i], value+DeathCounters[i], nocreate);
|
||||
}
|
||||
j+=j;
|
||||
}
|
||||
@@ -4026,8 +4016,8 @@ int Actor::LearnSpell(const ieResRef spellname, ieDword flags)
|
||||
// chance to learn roll
|
||||
int roll = LuckyRoll(1, 100, 0);
|
||||
// adjust the roll for specialist mages
|
||||
// doesn't work in bg1, since its spells don't have PrimaryType set
|
||||
if (GetKitIndex(BaseStats[IE_KIT])) {
|
||||
// doesn't work in bg1, since its spells don't have PrimaryType set (0 is NONE)
|
||||
if (GetKitIndex(BaseStats[IE_KIT]) && spell->PrimaryType) {
|
||||
if ((signed)BaseStats[IE_KIT] == 1<<(spell->PrimaryType+5)) { // +5 since the kit values start at 0x40
|
||||
roll += 15;
|
||||
} else {
|
||||
@@ -4063,7 +4053,7 @@ int Actor::LearnSpell(const ieResRef spellname, ieDword flags)
|
||||
if (tmp) {
|
||||
displaymsg->DisplayConstantStringName(tmp, 0xbcefbc, this);
|
||||
}
|
||||
if (flags&LS_ADDXP) {
|
||||
if (flags&LS_ADDXP && !(flags&LS_NOXP)) {
|
||||
int xp = CalculateExperience(XP_LEARNSPELL, explev);
|
||||
Game *game = core->GetGame();
|
||||
game->ShareXP(xp, SX_DIVIDE);
|
||||
@@ -4176,9 +4166,6 @@ int Actor::GetAttackStyle() const
|
||||
void Actor::AttackedBy( Actor *attacker)
|
||||
{
|
||||
LastAttacker = attacker->GetGlobalID();
|
||||
Game * game = core->GetGame();
|
||||
game->InAttack(GetGlobalID() );
|
||||
game->InAttack(LastAttacker);
|
||||
}
|
||||
|
||||
void Actor::SetTarget( Scriptable *target)
|
||||
@@ -4196,7 +4183,6 @@ void Actor::StopAttack()
|
||||
{
|
||||
SetStance(IE_ANI_READY);
|
||||
secondround = 0;
|
||||
core->GetGame()->OutAttack(GetGlobalID());
|
||||
InternalFlags|=IF_TARGETGONE; //this is for the trigger!
|
||||
if (InParty) {
|
||||
core->Autopause(AP_NOTARGET);
|
||||
@@ -4227,47 +4213,12 @@ void Actor::InitRound(ieDword gameTime)
|
||||
lastInit = gameTime;
|
||||
secondround = !secondround;
|
||||
|
||||
//roundTime will equal 0 if we aren't attacking something
|
||||
if (roundTime) {
|
||||
//only perform calculations at the beginning of the round!
|
||||
if (((gameTime-roundTime)%core->Time.round_size != 0) || \
|
||||
(roundTime == lastInit)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//reset variables used in PerformAttack
|
||||
attackcount = 0;
|
||||
attacksperround = 0;
|
||||
nextattack = 0;
|
||||
lastattack = 0;
|
||||
|
||||
//we set roundTime to zero on any of the following returns, because this
|
||||
//is guaranteed to be the start of a round, and we only want roundTime
|
||||
//if we are attacking this round
|
||||
if (InternalFlags&IF_STOPATTACK) {
|
||||
core->GetGame()->OutAttack(GetGlobalID());
|
||||
roundTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LastTarget) {
|
||||
StopAttack();
|
||||
roundTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//if held or disabled, etc, then cannot continue attacking
|
||||
ieDword state = GetStat(IE_STATE_ID);
|
||||
if (state&STATE_CANTMOVE) {
|
||||
roundTime = 0;
|
||||
return;
|
||||
}
|
||||
if (Immobile()) {
|
||||
roundTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//add one for second round to get an extra attack only if we
|
||||
//are x/2 attacks per round
|
||||
attackcount = GetStat(IE_NUMBEROFATTACKS);
|
||||
@@ -4488,8 +4439,9 @@ int Actor::GetToHit(int bonus, ieDword Flags, Actor *target) const
|
||||
|
||||
static const int weapon_damagetype[] = {DAMAGE_CRUSHING, DAMAGE_PIERCING,
|
||||
DAMAGE_CRUSHING, DAMAGE_SLASHING, DAMAGE_MISSILE, DAMAGE_STUNNING};
|
||||
static EffectRef fx_ac_vs_creature_type_ref = { "ACVsCreatureType", -1 };
|
||||
|
||||
int Actor::GetDefense(int DamageType) const
|
||||
int Actor::GetDefense(int DamageType, Actor *attacker) const
|
||||
{
|
||||
//specific damage type bonus.
|
||||
int defense = 0;
|
||||
@@ -4542,23 +4494,42 @@ int Actor::GetDefense(int DamageType) const
|
||||
defense += GetStat(IE_ARMORCLASS);
|
||||
}
|
||||
//Dexterity bonus is stored negative in 2da files.
|
||||
return defense + core->GetDexterityBonus(STAT_DEX_AC, GetStat(IE_DEX) );
|
||||
defense += core->GetDexterityBonus(STAT_DEX_AC, GetStat(IE_DEX) );
|
||||
if (attacker) {
|
||||
defense -= fxqueue.BonusAgainstCreature(fx_ac_vs_creature_type_ref,attacker);
|
||||
}
|
||||
return defense;
|
||||
}
|
||||
|
||||
|
||||
static EffectRef fx_ac_vs_creature_type_ref={"ACVsCreatureType",NULL,-1};
|
||||
|
||||
void Actor::PerformAttack(ieDword gameTime)
|
||||
{
|
||||
// start a new round if we really don't have one yet
|
||||
if (!roundTime) {
|
||||
printMessage("Actor", "Unregistered attack. We shouldn't be here?\n", RED);
|
||||
secondround = 0;
|
||||
if (InParty) {
|
||||
// TODO: this is temporary hack
|
||||
Game *game = core->GetGame();
|
||||
game->PartyAttack = true;
|
||||
}
|
||||
|
||||
// if held or disabled, etc, then cannot continue attacking
|
||||
// TODO: should be in action
|
||||
ieDword state = GetStat(IE_STATE_ID);
|
||||
if (state&STATE_CANTMOVE || Immobile()) {
|
||||
// this is also part of the UpdateActorState hack below. sorry!
|
||||
lastattack = gameTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!roundTime || (gameTime-roundTime > core->Time.round_size)) {
|
||||
// TODO: do we need cleverness for secondround here?
|
||||
InitRound(gameTime);
|
||||
}
|
||||
|
||||
//only return if we don't have any attacks left this round
|
||||
if (attackcount==0) return;
|
||||
if (attackcount==0) {
|
||||
// this is also part of the UpdateActorState hack below. sorry!
|
||||
lastattack = gameTime;
|
||||
return;
|
||||
}
|
||||
|
||||
// this check shouldn't be necessary, but it causes a divide-by-zero below,
|
||||
// so i would like it to be clear if it ever happens
|
||||
@@ -4712,8 +4683,7 @@ void Actor::PerformAttack(ieDword gameTime)
|
||||
|
||||
|
||||
//get target's defense against attack
|
||||
int defense = target->GetDefense(damagetype);
|
||||
defense -= target->fxqueue.BonusAgainstCreature(fx_ac_vs_creature_type_ref,this);
|
||||
int defense = target->GetDefense(damagetype, this);
|
||||
|
||||
bool success;
|
||||
if(ReverseToHit) {
|
||||
@@ -4739,10 +4709,10 @@ void Actor::PerformAttack(ieDword gameTime)
|
||||
ResetState();
|
||||
}
|
||||
|
||||
static EffectRef fx_stoneskin_ref={"StoneSkinModifier",NULL,-1};
|
||||
static EffectRef fx_stoneskin2_ref={"StoneSkin2Modifier",NULL,-1};
|
||||
static EffectRef fx_mirrorimage_ref={"MirrorImageModifier",NULL,-1};
|
||||
static EffectRef fx_aegis_ref={"Aegis",NULL,-1};
|
||||
static EffectRef fx_stoneskin_ref = { "StoneSkinModifier", -1 };
|
||||
static EffectRef fx_stoneskin2_ref = { "StoneSkin2Modifier", -1 };
|
||||
static EffectRef fx_mirrorimage_ref = { "MirrorImageModifier", -1 };
|
||||
static EffectRef fx_aegis_ref = { "Aegis", -1 };
|
||||
|
||||
void Actor::ModifyDamage(Actor *target, Scriptable *hitter, int &damage, int &resisted, int damagetype, WeaponInfo *wi, bool critical)
|
||||
{
|
||||
@@ -4886,10 +4856,8 @@ void Actor::UpdateActorState(ieDword gameTime) {
|
||||
StopAttack();
|
||||
} else {
|
||||
printMessage("Attack","(Leaving attack)", GREEN);
|
||||
core->GetGame()->OutAttack(GetGlobalID());
|
||||
}
|
||||
|
||||
roundTime = 0;
|
||||
lastattack = 0;
|
||||
}
|
||||
|
||||
@@ -5775,8 +5743,12 @@ void Actor::SetPortrait(const char* ResRef, int Which)
|
||||
}
|
||||
if(!Which) {
|
||||
for (i = 0; i < 8 && ResRef[i]; i++) {};
|
||||
SmallPortrait[i] = 'S';
|
||||
LargePortrait[i] = 'M';
|
||||
if (SmallPortrait[i-1] != 'S' && SmallPortrait[i-1] != 's') {
|
||||
SmallPortrait[i] = 'S';
|
||||
}
|
||||
if (LargePortrait[i-1] != 'M' && LargePortrait[i-1] != 'm') {
|
||||
LargePortrait[i] = 'M';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6047,16 +6019,16 @@ bool Actor::UseItemPoint(ieDword slot, ieDword header, const Point &target, ieDw
|
||||
ChargeItem(slot, header, item, itm, flags&UI_SILENT);
|
||||
gamedata->FreeItem(itm,tmpresref, false);
|
||||
if (pro) {
|
||||
pro->SetCaster(GetGlobalID());
|
||||
pro->SetCaster(GetGlobalID(), ITEM_CASTERLEVEL);
|
||||
GetCurrentArea()->AddProjectile(pro, Pos, target);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static EffectRef fx_damage_ref={"Damage",NULL,-1};
|
||||
static EffectRef fx_melee_ref={"SetMeleeEffect",NULL,-1};
|
||||
static EffectRef fx_ranged_ref={"SetRangedEffect",NULL,-1};
|
||||
static EffectRef fx_damage_ref = { "Damage", -1 };
|
||||
static EffectRef fx_melee_ref = { "SetMeleeEffect", -1 };
|
||||
static EffectRef fx_ranged_ref = { "SetRangedEffect", -1 };
|
||||
|
||||
bool Actor::UseItem(ieDword slot, ieDword header, Scriptable* target, ieDword flags, int damage)
|
||||
{
|
||||
@@ -6084,7 +6056,7 @@ bool Actor::UseItem(ieDword slot, ieDword header, Scriptable* target, ieDword fl
|
||||
gamedata->FreeItem(itm,tmpresref, false);
|
||||
if (pro) {
|
||||
//ieDword is unsigned!!
|
||||
pro->SetCaster(GetGlobalID());
|
||||
pro->SetCaster(GetGlobalID(), ITEM_CASTERLEVEL);
|
||||
if(((int)header < 0) && !(flags&UI_MISS)) { //using a weapon
|
||||
bool ranged = header == (ieDword)-2;
|
||||
ITMExtHeader *which = itm->GetWeaponHeader(ranged);
|
||||
@@ -6356,8 +6328,8 @@ int Actor::CheckUsability(Item *item) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EffectRef fx_cant_use_item_ref={"CantUseItem",NULL,-1};
|
||||
static EffectRef fx_cant_use_item_type_ref={"CantUseItemType",NULL,-1};
|
||||
static EffectRef fx_cant_use_item_ref = { "CantUseItem", -1 };
|
||||
static EffectRef fx_cant_use_item_type_ref = { "CantUseItemType", -1 };
|
||||
|
||||
//this one is the same, but returns strrefs based on effects
|
||||
ieStrRef Actor::Disabled(ieResRef name, ieDword type) const
|
||||
@@ -6891,7 +6863,7 @@ int Actor::LuckyRoll(int dice, int size, int add, ieDword flags, Actor* opponent
|
||||
return result + add;
|
||||
}
|
||||
|
||||
static EffectRef fx_remove_invisible_state_ref={"ForceVisible",NULL,-1};
|
||||
static EffectRef fx_remove_invisible_state_ref = { "ForceVisible", -1 };
|
||||
|
||||
// removes the (normal) invisibility state
|
||||
void Actor::CureInvisibility()
|
||||
@@ -6911,7 +6883,7 @@ void Actor::CureInvisibility()
|
||||
}
|
||||
}
|
||||
|
||||
static EffectRef fx_remove_sanctuary_ref={"Cure:Sanctuary",NULL,-1};
|
||||
static EffectRef fx_remove_sanctuary_ref = { "Cure:Sanctuary", -1 };
|
||||
|
||||
// removes the sanctuary effect
|
||||
void Actor::CureSanctuary()
|
||||
@@ -6979,7 +6951,7 @@ bool Actor::ModalSpellSkillCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
static EffectRef fx_disable_button_ref={ "DisableButton", NULL, -1 };
|
||||
static EffectRef fx_disable_button_ref = { "DisableButton", -1 };
|
||||
|
||||
inline void HideFailed(Actor* actor)
|
||||
{
|
||||
@@ -7004,9 +6976,20 @@ bool Actor::TryToHide() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the pc is in combat (seen / heard)
|
||||
Game *game = core->GetGame();
|
||||
if (game->PCInCombat(this)) {
|
||||
// check if the actor is seen by enemy
|
||||
Actor** visActors = GetCurrentArea()->GetAllActorsInRadius(Pos, GA_NO_DEAD, Modified[IE_VISUALRANGE]);
|
||||
Actor** poi = visActors;
|
||||
bool seen = false;
|
||||
while (*poi && !seen) {
|
||||
Actor *toCheck = *poi++;
|
||||
if (Modified[IE_EA] >= EA_EVILCUTOFF)
|
||||
seen = toCheck->Modified[IE_EA] < EA_EVILCUTOFF;
|
||||
else
|
||||
seen = toCheck->Modified[IE_EA] > EA_GOODCUTOFF;
|
||||
}
|
||||
free(visActors);
|
||||
|
||||
if (seen) {
|
||||
HideFailed(this);
|
||||
return false;
|
||||
}
|
||||
@@ -7018,6 +7001,7 @@ bool Actor::TryToHide() {
|
||||
skill = GetStat(IE_STEALTH);
|
||||
}
|
||||
|
||||
Game *game = core->GetGame();
|
||||
// check how bright our spot is
|
||||
ieDword lightness = game->GetCurrentArea()->GetLightLevel(Pos);
|
||||
// seems to be the color overlay at midnight; lightness of a point with rgb (200, 100, 100)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#ifndef ACTOR_H
|
||||
#define ACTOR_H
|
||||
|
||||
#include "Scriptable/ActorBlock.h"
|
||||
#include "Scriptable/Scriptable.h"
|
||||
|
||||
#include "Scriptable/PCStatStruct.h"
|
||||
|
||||
@@ -125,6 +125,9 @@ struct PolymorphCache;
|
||||
//added this comment for some clues if you really need it)
|
||||
//#define GA_GLOBAL 4096
|
||||
|
||||
//line of sight is ignored (for GetAllActorsInRadius)
|
||||
#define GA_NO_LOS 4096
|
||||
|
||||
// Detect() mode: IDS matching ignores invisibility
|
||||
#define GA_DETECT 8192
|
||||
|
||||
@@ -571,7 +574,7 @@ public:
|
||||
/* gets the to hit value */
|
||||
int GetToHit(int bonus, ieDword Flags, Actor *target) const;
|
||||
/* gets the defense against an attack */
|
||||
int GetDefense(int DamageType) const;
|
||||
int GetDefense(int DamageType, Actor *attacker) const;
|
||||
/* get the current hit bonus */
|
||||
bool GetCombatDetails(int &tohit, bool leftorright, WeaponInfo &wi, ITMExtHeader *&header, ITMExtHeader *&hittingheader,\
|
||||
ieDword &Flags, int &DamageBonus, int &speed, int &CriticalBonus, int &style, Actor *target) const;
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003-2005 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Scriptable/Container.h"
|
||||
|
||||
#include "strrefs.h"
|
||||
#include "win32def.h"
|
||||
|
||||
#include "Audio.h"
|
||||
#include "DisplayMessage.h"
|
||||
#include "Game.h"
|
||||
#include "GameData.h"
|
||||
#include "Interface.h"
|
||||
#include "Item.h"
|
||||
#include "Map.h"
|
||||
#include "Projectile.h"
|
||||
#include "Spell.h"
|
||||
#include "SpriteCover.h"
|
||||
#include "TileMap.h"
|
||||
#include "Video.h"
|
||||
#include "GameScript/GSUtils.h"
|
||||
#include "GUI/GameControl.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#define YESNO(x) ( (x)?"Yes":"No")
|
||||
|
||||
Container::Container(void)
|
||||
: Highlightable( ST_CONTAINER )
|
||||
{
|
||||
Type = 0;
|
||||
LockDifficulty = 0;
|
||||
Flags = 0;
|
||||
TrapDetectionDiff = 0;
|
||||
TrapRemovalDiff = 0;
|
||||
Trapped = 0;
|
||||
TrapDetected = 0;
|
||||
inventory.SetInventoryType(INVENTORY_HEAP);
|
||||
// NULL should be 0 for this
|
||||
memset (groundicons, 0, sizeof(groundicons) );
|
||||
groundiconcover = 0;
|
||||
}
|
||||
|
||||
void Container::FreeGroundIcons()
|
||||
{
|
||||
Video* video = core->GetVideoDriver();
|
||||
|
||||
for (int i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
|
||||
if (groundicons[i]) {
|
||||
video->FreeSprite( groundicons[i] );
|
||||
groundicons[i]=NULL;
|
||||
}
|
||||
}
|
||||
delete groundiconcover;
|
||||
groundiconcover = 0;
|
||||
}
|
||||
|
||||
Container::~Container()
|
||||
{
|
||||
FreeGroundIcons();
|
||||
}
|
||||
|
||||
void Container::DrawPile(bool highlight, Region screen, Color tint)
|
||||
{
|
||||
Video* video = core->GetVideoDriver();
|
||||
CreateGroundIconCover();
|
||||
for (int i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
|
||||
if (groundicons[i]) {
|
||||
//draw it with highlight
|
||||
video->BlitGameSprite(groundicons[i],
|
||||
screen.x + Pos.x, screen.y + Pos.y,
|
||||
BLIT_TINTED | (highlight ? 0:BLIT_NOSHADOW),
|
||||
tint, groundiconcover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the SpriteCover for the groundicons
|
||||
void Container::CreateGroundIconCover()
|
||||
{
|
||||
int xpos = 0;
|
||||
int ypos = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
int i; //msvc6.0
|
||||
for (i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
|
||||
if (groundicons[i]) {
|
||||
Sprite2D& spr = *groundicons[i];
|
||||
if (xpos < spr.XPos) {
|
||||
width += spr.XPos - xpos;
|
||||
xpos = spr.XPos;
|
||||
}
|
||||
if (ypos < spr.YPos) {
|
||||
height += spr.YPos - ypos;
|
||||
ypos = spr.YPos;
|
||||
}
|
||||
if (width-xpos < spr.Width-spr.XPos) {
|
||||
width = spr.Width-spr.XPos+xpos;
|
||||
}
|
||||
if (height-ypos < spr.Height-spr.YPos) {
|
||||
height = spr.Height-spr.YPos+ypos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!groundiconcover ||
|
||||
!groundiconcover->Covers(Pos.x, Pos.y, xpos, ypos, width, height))
|
||||
{
|
||||
delete groundiconcover;
|
||||
groundiconcover = 0;
|
||||
if (width*height > 0) {
|
||||
groundiconcover = GetCurrentArea()->BuildSpriteCover
|
||||
(Pos.x, Pos.y, xpos, ypos, width, height, WantDither());
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// TODO: remove this checking code eventually
|
||||
for (i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
|
||||
if (groundicons[i]) {
|
||||
Sprite2D& spr = *groundicons[i];
|
||||
assert(groundiconcover->Covers(Pos.x, Pos.y, spr.XPos, spr.YPos, spr.Width, spr.Height));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Container::SetContainerLocked(bool lock)
|
||||
{
|
||||
if (lock) {
|
||||
Flags|=CONT_LOCKED;
|
||||
} else {
|
||||
Flags&=~CONT_LOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
//This function doesn't exist in the original IE, destroys a container
|
||||
//turning it to a ground pile
|
||||
void Container::DestroyContainer()
|
||||
{
|
||||
//it is already a groundpile?
|
||||
if (Type == IE_CONTAINER_PILE)
|
||||
return;
|
||||
Type = IE_CONTAINER_PILE;
|
||||
RefreshGroundIcons();
|
||||
//probably we should stop the script or trigger it, whatever
|
||||
}
|
||||
|
||||
//Takes an item from the container's inventory and returns its pointer
|
||||
CREItem *Container::RemoveItem(unsigned int idx, unsigned int count)
|
||||
{
|
||||
CREItem *ret = inventory.RemoveItem(idx, count);
|
||||
//we just took the 3. or less item, groundpile changed
|
||||
if ((Type == IE_CONTAINER_PILE) && (inventory.GetSlotCount()<3)) {
|
||||
RefreshGroundIcons();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Adds an item to the container's inventory
|
||||
//containers always have enough capacity (so far), thus we always return 2
|
||||
int Container::AddItem(CREItem *item)
|
||||
{
|
||||
inventory.AddItem(item);
|
||||
//we just added a 3. or less item, groundpile changed
|
||||
if ((Type == IE_CONTAINER_PILE) && (inventory.GetSlotCount()<4)) {
|
||||
RefreshGroundIcons();
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
void Container::RefreshGroundIcons()
|
||||
{
|
||||
int i = inventory.GetSlotCount();
|
||||
if (i>MAX_GROUND_ICON_DRAWN)
|
||||
i = MAX_GROUND_ICON_DRAWN;
|
||||
FreeGroundIcons();
|
||||
while (i--) {
|
||||
CREItem *slot = inventory.GetSlotItem(i); //borrowed reference
|
||||
Item *itm = gamedata->GetItem( slot->ItemResRef ); //cached reference
|
||||
//well, this is required in PST, needs more work if some other
|
||||
//game is broken by not using -1,0
|
||||
groundicons[i] = gamedata->GetBAMSprite( itm->GroundIcon, 0, 0 );
|
||||
gamedata->FreeItem( itm, slot->ItemResRef ); //decref
|
||||
}
|
||||
}
|
||||
|
||||
//used for ground piles
|
||||
int Container::WantDither()
|
||||
{
|
||||
//if pile is highlighted, always dither it
|
||||
if (Highlight) {
|
||||
return 2; //dither me if you want
|
||||
}
|
||||
//if pile isn't highlighted, dither it if the polygon wants
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Container::IsOpen() const
|
||||
{
|
||||
if (Flags&CONT_LOCKED) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Container::TryPickLock(Actor *actor)
|
||||
{
|
||||
if (LockDifficulty == 100) {
|
||||
if (OpenFail != (ieDword)-1) {
|
||||
displaymsg->DisplayStringName(OpenFail, 0xbcefbc, actor, IE_STR_SOUND|IE_STR_SPEECH);
|
||||
} else {
|
||||
displaymsg->DisplayConstantStringName(STR_CONT_NOPICK, 0xbcefbc, actor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (actor->GetStat(IE_LOCKPICKING)<LockDifficulty) {
|
||||
displaymsg->DisplayConstantStringName(STR_LOCKPICK_FAILED, 0xbcefbc, actor);
|
||||
LastPickLockFailed = actor->GetGlobalID();
|
||||
return;
|
||||
}
|
||||
SetContainerLocked(false);
|
||||
displaymsg->DisplayConstantStringName(STR_LOCKPICK_DONE, 0xd7d7be, actor);
|
||||
LastUnlocked = actor->GetGlobalID();
|
||||
ImmediateEvent();
|
||||
int xp = actor->CalculateExperience(XP_LOCKPICK, actor->GetXPLevel(1));
|
||||
Game *game = core->GetGame();
|
||||
game->ShareXP(xp, SX_DIVIDE);
|
||||
}
|
||||
|
||||
void Container::TryBashLock(Actor *actor)
|
||||
{
|
||||
//Get the strength bonus agains lock difficulty
|
||||
int str = actor->GetStat(IE_STR);
|
||||
int strEx = actor->GetStat(IE_STREXTRA);
|
||||
unsigned int bonus = core->GetStrengthBonus(2, str, strEx); //BEND_BARS_LIFT_GATES
|
||||
unsigned int roll = actor->LuckyRoll(1, 10, bonus, 0);
|
||||
|
||||
if(roll < LockDifficulty || LockDifficulty == 100) {
|
||||
displaymsg->DisplayConstantStringName(STR_CONTBASH_FAIL, 0xbcefbc, actor);
|
||||
return;
|
||||
}
|
||||
|
||||
displaymsg->DisplayConstantStringName(STR_CONTBASH_DONE, 0xd7d7be, actor);
|
||||
SetContainerLocked(false);
|
||||
//Is this really useful ?
|
||||
LastUnlocked = actor->GetGlobalID();
|
||||
ImmediateEvent();
|
||||
}
|
||||
|
||||
void Container::DebugDump() const
|
||||
{
|
||||
printf( "Debugdump of Container %s\n", GetScriptName() );
|
||||
printf( "Container Global ID: %d\n", GetGlobalID());
|
||||
printf( "Position: %d.%d\n", Pos.x, Pos.y);
|
||||
printf( "Type: %d, Locked: %s, LockDifficulty: %d\n", Type, YESNO(Flags&CONT_LOCKED), LockDifficulty );
|
||||
printf( "Flags: %d, Trapped: %s, Detected: %d\n", Flags, YESNO(Trapped), TrapDetected );
|
||||
printf( "Trap detection: %d%%, Trap removal: %d%%\n", TrapDetectionDiff,
|
||||
TrapRemovalDiff );
|
||||
const char *name = "NONE";
|
||||
if (Scripts[0]) {
|
||||
name = Scripts[0]->GetName();
|
||||
}
|
||||
printf( "Script: %s, Key: %s\n", name, KeyResRef );
|
||||
// FIXME: const_cast
|
||||
const_cast<Inventory&>(inventory).dump();
|
||||
}
|
||||
|
||||
bool Container::TryUnlock(Actor *actor) {
|
||||
if (!(Flags&CONT_LOCKED)) return true;
|
||||
|
||||
return Highlightable::TryUnlock(actor, false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONTAINER_H
|
||||
#define CONTAINER_H
|
||||
|
||||
#include "Scriptable.h"
|
||||
|
||||
//container flags
|
||||
#define CONT_LOCKED 1
|
||||
#define CONT_RESET 8
|
||||
#define CONT_DISABLED 32
|
||||
|
||||
class GEM_EXPORT Container : public Highlightable {
|
||||
public:
|
||||
Container(void);
|
||||
~Container(void);
|
||||
void SetContainerLocked(bool lock);
|
||||
//turns the container to a pile
|
||||
void DestroyContainer();
|
||||
//removes an item from the container's inventory
|
||||
CREItem *RemoveItem(unsigned int idx, unsigned int count);
|
||||
//adds an item to the container's inventory
|
||||
int AddItem(CREItem *item);
|
||||
//draws the ground icons
|
||||
void DrawPile(bool highlight, Region screen, Color tint);
|
||||
//returns dithering option
|
||||
int WantDither();
|
||||
int IsOpen() const;
|
||||
void TryPickLock(Actor *actor);
|
||||
void TryBashLock(Actor* actor) ;
|
||||
bool TryUnlock(Actor *actor);
|
||||
void DebugDump() const;
|
||||
int TrapResets() const { return Flags & CONT_RESET; }
|
||||
private:
|
||||
//updates the ground icons for a pile
|
||||
void RefreshGroundIcons();
|
||||
void FreeGroundIcons();
|
||||
void CreateGroundIconCover();
|
||||
public:
|
||||
Point toOpen;
|
||||
ieWord Type;
|
||||
ieDword Flags;
|
||||
ieWord LockDifficulty;
|
||||
Inventory inventory;
|
||||
ieStrRef OpenFail;
|
||||
//these are not saved
|
||||
Sprite2D *groundicons[3];
|
||||
SpriteCover *groundiconcover;
|
||||
//keyresref is stored in Highlightable
|
||||
};
|
||||
|
||||
#endif
|
||||
409
project/jni/application/gemrb/gemrb/core/Scriptable/Door.cpp
Normal file
409
project/jni/application/gemrb/gemrb/core/Scriptable/Door.cpp
Normal file
@@ -0,0 +1,409 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003-2005 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Scriptable/Door.h"
|
||||
|
||||
#include "strrefs.h"
|
||||
#include "win32def.h"
|
||||
|
||||
#include "Audio.h"
|
||||
#include "DisplayMessage.h"
|
||||
#include "Game.h"
|
||||
#include "GameData.h"
|
||||
#include "Interface.h"
|
||||
#include "Item.h"
|
||||
#include "Map.h"
|
||||
#include "Projectile.h"
|
||||
#include "Spell.h"
|
||||
#include "SpriteCover.h"
|
||||
#include "TileMap.h"
|
||||
#include "Video.h"
|
||||
#include "GameScript/GSUtils.h"
|
||||
#include "GUI/GameControl.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#define YESNO(x) ( (x)?"Yes":"No")
|
||||
|
||||
Door::Door(TileOverlay* Overlay)
|
||||
: Highlightable( ST_DOOR )
|
||||
{
|
||||
tiles = NULL;
|
||||
tilecount = 0;
|
||||
Flags = 0;
|
||||
open = NULL;
|
||||
closed = NULL;
|
||||
open_ib = NULL;
|
||||
oibcount = 0;
|
||||
closed_ib = NULL;
|
||||
cibcount = 0;
|
||||
OpenSound[0] = 0;
|
||||
CloseSound[0] = 0;
|
||||
LockSound[0] = 0;
|
||||
UnLockSound[0] = 0;
|
||||
overlay = Overlay;
|
||||
LinkedInfo[0] = 0;
|
||||
OpenStrRef = (ieDword) -1;
|
||||
}
|
||||
|
||||
Door::~Door(void)
|
||||
{
|
||||
if (Flags&DOOR_OPEN) {
|
||||
if (closed) {
|
||||
delete( closed );
|
||||
}
|
||||
} else {
|
||||
if (open) {
|
||||
delete( open );
|
||||
}
|
||||
}
|
||||
if (tiles) {
|
||||
free( tiles );
|
||||
}
|
||||
if (open_ib) {
|
||||
free( open_ib );
|
||||
}
|
||||
if (closed_ib) {
|
||||
free( closed_ib );
|
||||
}
|
||||
}
|
||||
|
||||
void Door::ImpedeBlocks(int count, Point *points, unsigned char value)
|
||||
{
|
||||
for(int i = 0;i<count;i++) {
|
||||
unsigned char tmp = area->GetInternalSearchMap(points[i].x, points[i].y) & PATH_MAP_NOTDOOR;
|
||||
area->SetInternalSearchMap(points[i].x, points[i].y, tmp|value);
|
||||
}
|
||||
}
|
||||
|
||||
void Door::UpdateDoor()
|
||||
{
|
||||
if (Flags&DOOR_OPEN) {
|
||||
outline = open;
|
||||
} else {
|
||||
outline = closed;
|
||||
}
|
||||
// update the Scriptable position
|
||||
Pos.x = outline->BBox.x + outline->BBox.w/2;
|
||||
Pos.y = outline->BBox.y + outline->BBox.h/2;
|
||||
|
||||
unsigned char oval, cval;
|
||||
oval = PATH_MAP_IMPASSABLE;
|
||||
if (Flags & DOOR_TRANSPARENT) {
|
||||
cval = PATH_MAP_DOOR_IMPASSABLE;
|
||||
}
|
||||
else {
|
||||
//both door flags are needed here, one for transparency the other
|
||||
//is for passability
|
||||
cval = PATH_MAP_DOOR_OPAQUE|PATH_MAP_DOOR_IMPASSABLE;
|
||||
}
|
||||
if (Flags &DOOR_OPEN) {
|
||||
ImpedeBlocks(cibcount, closed_ib, 0);
|
||||
ImpedeBlocks(oibcount, open_ib, cval);
|
||||
}
|
||||
else {
|
||||
ImpedeBlocks(oibcount, open_ib, 0);
|
||||
ImpedeBlocks(cibcount, closed_ib, cval);
|
||||
}
|
||||
|
||||
InfoPoint *ip = area->TMap->GetInfoPoint(LinkedInfo);
|
||||
if (ip) {
|
||||
if (Flags&DOOR_OPEN) ip->Flags&=~INFO_DOOR;
|
||||
else ip->Flags|=INFO_DOOR;
|
||||
}
|
||||
}
|
||||
|
||||
void Door::ToggleTiles(int State, int playsound)
|
||||
{
|
||||
int i;
|
||||
int state;
|
||||
|
||||
if (State) {
|
||||
state = !closedIndex;
|
||||
if (playsound && ( OpenSound[0] != '\0' ))
|
||||
core->GetAudioDrv()->Play( OpenSound );
|
||||
} else {
|
||||
state = closedIndex;
|
||||
if (playsound && ( CloseSound[0] != '\0' ))
|
||||
core->GetAudioDrv()->Play( CloseSound );
|
||||
}
|
||||
for (i = 0; i < tilecount; i++) {
|
||||
overlay->tiles[tiles[i]]->tileIndex = (ieByte) state;
|
||||
}
|
||||
|
||||
//set door_open as state
|
||||
Flags = (Flags & ~DOOR_OPEN) | (State == !core->HasFeature(GF_REVERSE_DOOR) );
|
||||
}
|
||||
|
||||
//this is the short name (not the scripting name)
|
||||
void Door::SetName(const char* name)
|
||||
{
|
||||
strnlwrcpy( ID, name, 8 );
|
||||
}
|
||||
|
||||
void Door::SetTiles(unsigned short* Tiles, int cnt)
|
||||
{
|
||||
if (tiles) {
|
||||
free( tiles );
|
||||
}
|
||||
tiles = Tiles;
|
||||
tilecount = cnt;
|
||||
}
|
||||
|
||||
void Door::SetDoorLocked(int Locked, int playsound)
|
||||
{
|
||||
if (Locked) {
|
||||
if (Flags & DOOR_LOCKED) return;
|
||||
Flags|=DOOR_LOCKED;
|
||||
if (playsound && ( LockSound[0] != '\0' ))
|
||||
core->GetAudioDrv()->Play( LockSound );
|
||||
}
|
||||
else {
|
||||
if (!(Flags & DOOR_LOCKED)) return;
|
||||
Flags&=~DOOR_LOCKED;
|
||||
if (playsound && ( UnLockSound[0] != '\0' ))
|
||||
core->GetAudioDrv()->Play( UnLockSound );
|
||||
}
|
||||
}
|
||||
|
||||
int Door::IsOpen() const
|
||||
{
|
||||
int ret = core->HasFeature(GF_REVERSE_DOOR);
|
||||
if (Flags&DOOR_OPEN) {
|
||||
ret = !ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//also mark actors to fix position
|
||||
bool Door::BlockedOpen(int Open, int ForceOpen)
|
||||
{
|
||||
bool blocked;
|
||||
int count;
|
||||
Point *points;
|
||||
|
||||
blocked = false;
|
||||
if (Open) {
|
||||
count = oibcount;
|
||||
points = open_ib;
|
||||
} else {
|
||||
count = cibcount;
|
||||
points = closed_ib;
|
||||
}
|
||||
//getting all impeded actors flagged for jump
|
||||
Region rgn;
|
||||
rgn.w = 16;
|
||||
rgn.h = 12;
|
||||
for(int i = 0;i<count;i++) {
|
||||
Actor** ab;
|
||||
rgn.x = points[i].x*16;
|
||||
rgn.y = points[i].y*12;
|
||||
unsigned char tmp = area->GetInternalSearchMap(points[i].x, points[i].y) & PATH_MAP_ACTOR;
|
||||
if (tmp) {
|
||||
int ac = area->GetActorInRect(ab, rgn, false);
|
||||
while(ac--) {
|
||||
if (ab[ac]->GetBase(IE_DONOTJUMP)) {
|
||||
continue;
|
||||
}
|
||||
ab[ac]->SetBase(IE_DONOTJUMP, DNJ_JUMP);
|
||||
blocked = true;
|
||||
}
|
||||
if (ab) {
|
||||
free(ab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((Flags&DOOR_SLIDE) || ForceOpen) {
|
||||
return false;
|
||||
}
|
||||
return blocked;
|
||||
}
|
||||
|
||||
void Door::SetDoorOpen(int Open, int playsound, ieDword ID)
|
||||
{
|
||||
if (playsound) {
|
||||
//the door cannot be blocked when opening,
|
||||
//but the actors will be pushed
|
||||
//BlockedOpen will mark actors to be pushed
|
||||
if (BlockedOpen(Open,0) && !Open) {
|
||||
//clear up the blocking actors
|
||||
area->JumpActors(false);
|
||||
return;
|
||||
}
|
||||
area->JumpActors(true);
|
||||
}
|
||||
if (Open) {
|
||||
LastEntered = ID; //used as lastOpener
|
||||
|
||||
// in PS:T, opening a door does not unlock it
|
||||
if (!core->HasFeature(GF_REVERSE_DOOR)) {
|
||||
SetDoorLocked(false,playsound);
|
||||
}
|
||||
} else {
|
||||
LastTriggerObject = LastTrigger = ID; //used as lastCloser
|
||||
}
|
||||
ToggleTiles(Open, playsound);
|
||||
//synchronising other data with the door state
|
||||
UpdateDoor();
|
||||
area->ActivateWallgroups(open_wg_index, open_wg_count, Flags&DOOR_OPEN);
|
||||
area->ActivateWallgroups(closed_wg_index, closed_wg_count, !(Flags&DOOR_OPEN));
|
||||
core->SetEventFlag(EF_TARGETMODE);
|
||||
}
|
||||
|
||||
bool Door::TryUnlock(Actor *actor) {
|
||||
if (!(Flags&DOOR_LOCKED)) return true;
|
||||
|
||||
// don't remove key in PS:T!
|
||||
bool removekey = !core->HasFeature(GF_REVERSE_DOOR) && Flags&DOOR_KEY;
|
||||
return Highlightable::TryUnlock(actor, removekey);
|
||||
}
|
||||
|
||||
void Door::TryDetectSecret(int skill)
|
||||
{
|
||||
if (Type != ST_DOOR) return;
|
||||
if (Visible()) return;
|
||||
if (skill > (signed)DiscoveryDiff) {
|
||||
Flags |= DOOR_FOUND;
|
||||
core->PlaySound(DS_FOUNDSECRET);
|
||||
}
|
||||
}
|
||||
|
||||
// return true if the door isn't secret or if it is, but was already discovered
|
||||
bool Door::Visible()
|
||||
{
|
||||
return (!(Flags & DOOR_SECRET) || (Flags & DOOR_FOUND));
|
||||
}
|
||||
|
||||
void Door::SetPolygon(bool Open, Gem_Polygon* poly)
|
||||
{
|
||||
if (Open) {
|
||||
if (open)
|
||||
delete( open );
|
||||
open = poly;
|
||||
} else {
|
||||
if (closed)
|
||||
delete( closed );
|
||||
closed = poly;
|
||||
}
|
||||
}
|
||||
|
||||
void Door::SetNewOverlay(TileOverlay *Overlay) {
|
||||
overlay = Overlay;
|
||||
ToggleTiles(IsOpen(), false);
|
||||
}
|
||||
|
||||
void Highlightable::SetTrapDetected(int x)
|
||||
{
|
||||
if(x == TrapDetected)
|
||||
return;
|
||||
TrapDetected = x;
|
||||
if(TrapDetected) {
|
||||
core->Autopause(AP_TRAP);
|
||||
}
|
||||
}
|
||||
|
||||
void Highlightable::TryDisarm(Actor *actor)
|
||||
{
|
||||
if (!Trapped || !TrapDetected) return;
|
||||
|
||||
LastTriggerObject = LastTrigger = actor->GetGlobalID();
|
||||
int skill = actor->GetStat(IE_TRAPS);
|
||||
|
||||
if (skill/2+core->Roll(1,skill/2,0)>TrapRemovalDiff) {
|
||||
LastDisarmed = actor->GetGlobalID();
|
||||
//trap removed
|
||||
Trapped = 0;
|
||||
displaymsg->DisplayConstantStringName(STR_DISARM_DONE, 0xd7d7be, actor);
|
||||
int xp = actor->CalculateExperience(XP_DISARM, actor->GetXPLevel(1));
|
||||
Game *game = core->GetGame();
|
||||
game->ShareXP(xp, SX_DIVIDE);
|
||||
} else {
|
||||
displaymsg->DisplayConstantStringName(STR_DISARM_FAIL, 0xd7d7be, actor);
|
||||
TriggerTrap(skill, LastTrigger);
|
||||
}
|
||||
ImmediateEvent();
|
||||
}
|
||||
|
||||
void Door::TryPickLock(Actor *actor)
|
||||
{
|
||||
if (LockDifficulty == 100) {
|
||||
if (OpenStrRef != (ieDword)-1) {
|
||||
displaymsg->DisplayStringName(OpenStrRef, 0xbcefbc, actor, IE_STR_SOUND|IE_STR_SPEECH);
|
||||
} else {
|
||||
displaymsg->DisplayConstantStringName(STR_DOOR_NOPICK, 0xbcefbc, actor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (actor->GetStat(IE_LOCKPICKING)<LockDifficulty) {
|
||||
displaymsg->DisplayConstantStringName(STR_LOCKPICK_FAILED, 0xbcefbc, actor);
|
||||
LastPickLockFailed = actor->GetGlobalID();
|
||||
return;
|
||||
}
|
||||
SetDoorLocked( false, true);
|
||||
displaymsg->DisplayConstantStringName(STR_LOCKPICK_DONE, 0xd7d7be, actor);
|
||||
LastUnlocked = actor->GetGlobalID();
|
||||
ImmediateEvent();
|
||||
int xp = actor->CalculateExperience(XP_LOCKPICK, actor->GetXPLevel(1));
|
||||
Game *game = core->GetGame();
|
||||
game->ShareXP(xp, SX_DIVIDE);
|
||||
}
|
||||
|
||||
void Door::TryBashLock(Actor *actor)
|
||||
{
|
||||
//Get the strength bonus agains lock difficulty
|
||||
int str = actor->GetStat(IE_STR);
|
||||
int strEx = actor->GetStat(IE_STREXTRA);
|
||||
unsigned int bonus = core->GetStrengthBonus(2, str, strEx); //BEND_BARS_LIFT_GATES
|
||||
unsigned int roll = actor->LuckyRoll(1, 10, bonus, 0);
|
||||
|
||||
if(roll < LockDifficulty || LockDifficulty == 100) {
|
||||
displaymsg->DisplayConstantStringName(STR_DOORBASH_FAIL, 0xbcefbc, actor);
|
||||
return;
|
||||
}
|
||||
|
||||
displaymsg->DisplayConstantStringName(STR_DOORBASH_DONE, 0xd7d7be, actor);
|
||||
SetDoorLocked(false, true);
|
||||
//Is this really useful ?
|
||||
LastUnlocked = actor->GetGlobalID();
|
||||
ImmediateEvent();
|
||||
}
|
||||
|
||||
void Door::DebugDump() const
|
||||
{
|
||||
printf( "Debugdump of Door %s:\n", GetScriptName() );
|
||||
printf( "Door Global ID: %d\n", GetGlobalID());
|
||||
printf( "Position: %d.%d\n", Pos.x, Pos.y);
|
||||
printf( "Door Open: %s\n", YESNO(IsOpen()));
|
||||
printf( "Door Locked: %s\n", YESNO(Flags&DOOR_LOCKED));
|
||||
printf( "Door Trapped: %s\n", YESNO(Trapped));
|
||||
if (Trapped) {
|
||||
printf( "Trap Permanent: %s Detectable: %s\n", YESNO(Flags&DOOR_RESET), YESNO(Flags&DOOR_DETECTABLE) );
|
||||
}
|
||||
printf( "Secret door: %s (Found: %s)\n", YESNO(Flags&DOOR_SECRET),YESNO(Flags&DOOR_FOUND));
|
||||
const char *Key = GetKey();
|
||||
const char *name = "NONE";
|
||||
if (Scripts[0]) {
|
||||
name = Scripts[0]->GetName();
|
||||
}
|
||||
printf( "Script: %s, Key (%s) removed: %s, Dialog: %s\n", name, Key?Key:"NONE", YESNO(Flags&DOOR_KEY), Dialog );
|
||||
}
|
||||
|
||||
98
project/jni/application/gemrb/gemrb/core/Scriptable/Door.h
Normal file
98
project/jni/application/gemrb/gemrb/core/Scriptable/Door.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DOOR_H
|
||||
#define DOOR_H
|
||||
|
||||
#include "Scriptable.h"
|
||||
|
||||
//door flags
|
||||
#define DOOR_OPEN 1
|
||||
#define DOOR_LOCKED 2
|
||||
#define DOOR_RESET 4 //reset trap
|
||||
#define DOOR_DETECTABLE 8 //trap detectable
|
||||
#define DOOR_16 16 //unknown
|
||||
#define DOOR_32 32 //unknown
|
||||
#define DOOR_LINKED 64 //info trigger linked to this door
|
||||
#define DOOR_SECRET 128 //door is secret
|
||||
#define DOOR_FOUND 256 //secret door found
|
||||
#define DOOR_TRANSPARENT 512 //obscures vision
|
||||
#define DOOR_KEY 1024 //key removed when used
|
||||
#define DOOR_SLIDE 2048 //impeded blocks ignored
|
||||
|
||||
class GEM_EXPORT Door : public Highlightable {
|
||||
public:
|
||||
Door(TileOverlay* Overlay);
|
||||
~Door(void);
|
||||
public:
|
||||
ieVariable LinkedInfo;
|
||||
ieResRef ID; //WED ID
|
||||
TileOverlay* overlay;
|
||||
unsigned short* tiles;
|
||||
int tilecount;
|
||||
ieDword Flags;
|
||||
int closedIndex;
|
||||
//trigger areas
|
||||
Gem_Polygon* open;
|
||||
Gem_Polygon* closed;
|
||||
//impeded blocks
|
||||
Point* open_ib; //impeded blocks stored in a Point array
|
||||
int oibcount;
|
||||
Point* closed_ib;
|
||||
int cibcount;
|
||||
//wallgroup covers
|
||||
unsigned int open_wg_index;
|
||||
unsigned int open_wg_count;
|
||||
unsigned int closed_wg_index;
|
||||
unsigned int closed_wg_count;
|
||||
Point toOpen[2];
|
||||
ieResRef OpenSound;
|
||||
ieResRef CloseSound;
|
||||
ieResRef LockSound;
|
||||
ieResRef UnLockSound;
|
||||
ieDword DiscoveryDiff;
|
||||
ieDword LockDifficulty; //this is a dword?
|
||||
ieStrRef OpenStrRef;
|
||||
ieStrRef NameStrRef;
|
||||
ieDword Unknown54; //unused in tob
|
||||
private:
|
||||
void SetWallgroups(int count, int value);
|
||||
void ImpedeBlocks(int count, Point *points, unsigned char value);
|
||||
void UpdateDoor();
|
||||
bool BlockedOpen(int Open, int ForceOpen);
|
||||
public:
|
||||
void ToggleTiles(int State, int playsound = false);
|
||||
void SetName(const char* Name); // sets door ID
|
||||
void SetTiles(unsigned short* Tiles, int count);
|
||||
void SetDoorLocked(int Locked, int playsound);
|
||||
void SetDoorOpen(int Open, int playsound, ieDword ID);
|
||||
void SetPolygon(bool Open, Gem_Polygon* poly);
|
||||
int IsOpen() const;
|
||||
void TryPickLock(Actor *actor);
|
||||
void TryBashLock(Actor* actor) ;
|
||||
bool TryUnlock(Actor *actor);
|
||||
void TryDetectSecret(int skill);
|
||||
bool Visible();
|
||||
void DebugDump() const;
|
||||
int TrapResets() const { return Flags & DOOR_RESET; }
|
||||
void SetNewOverlay(TileOverlay *Overlay);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,272 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003-2005 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
#include "strrefs.h"
|
||||
#include "win32def.h"
|
||||
|
||||
#include "Audio.h"
|
||||
#include "DisplayMessage.h"
|
||||
#include "Game.h"
|
||||
#include "GameData.h"
|
||||
#include "Interface.h"
|
||||
#include "Item.h"
|
||||
#include "Map.h"
|
||||
#include "Projectile.h"
|
||||
#include "Spell.h"
|
||||
#include "SpriteCover.h"
|
||||
#include "TileMap.h"
|
||||
#include "Video.h"
|
||||
#include "GameScript/GSUtils.h"
|
||||
#include "GUI/GameControl.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#define YESNO(x) ( (x)?"Yes":"No")
|
||||
|
||||
InfoPoint::InfoPoint(void)
|
||||
: Highlightable( ST_TRIGGER )
|
||||
{
|
||||
Destination[0] = 0;
|
||||
EntranceName[0] = 0;
|
||||
Flags = 0;
|
||||
TrapDetectionDiff = 0;
|
||||
TrapRemovalDiff = 0;
|
||||
TrapDetected = 0;
|
||||
TrapLaunch.empty();
|
||||
EnterWav[0] = 0;
|
||||
}
|
||||
|
||||
InfoPoint::~InfoPoint(void)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoPoint::SetEnter(const char *resref)
|
||||
{
|
||||
if (gamedata->Exists(resref, IE_WAV_CLASS_ID) ) {
|
||||
strnuprcpy(EnterWav, resref, 8);
|
||||
}
|
||||
}
|
||||
|
||||
//checks if the actor may use this travel trigger
|
||||
//bit 1 : can use
|
||||
//bit 2 : whole team
|
||||
int InfoPoint::CheckTravel(Actor *actor)
|
||||
{
|
||||
if (Flags&TRAP_DEACTIVATED) return CT_CANTMOVE;
|
||||
if (!actor->InParty && (Flags&TRAVEL_NONPC) ) return CT_CANTMOVE;
|
||||
if (actor->InParty && (Flags&TRAVEL_PARTY) ) {
|
||||
if (core->HasFeature(GF_TEAM_MOVEMENT) || core->GetGame()->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, ENP_CANMOVE) ) {
|
||||
return CT_WHOLE;
|
||||
}
|
||||
return CT_GO_CLOSER;
|
||||
}
|
||||
if(actor->IsSelected() ) {
|
||||
if(core->GetGame()->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, ENP_CANMOVE|ENP_ONLYSELECT) ) {
|
||||
return CT_MOVE_SELECTED;
|
||||
}
|
||||
return CT_SELECTED;
|
||||
}
|
||||
return CT_ACTIVE;
|
||||
}
|
||||
|
||||
//detect this trap, using a skill, skill could be set to 256 for 'sure'
|
||||
//skill is the all around modified trap detection skill
|
||||
//a trapdetectiondifficulty of 100 means impossible detection short of a spell
|
||||
void Highlightable::DetectTrap(int skill)
|
||||
{
|
||||
if (!CanDetectTrap()) return;
|
||||
if (!Scripts[0]) return;
|
||||
if ((skill>=100) && (skill!=256) ) skill = 100;
|
||||
if (skill/2+core->Roll(1,skill/2,0)>TrapDetectionDiff) {
|
||||
SetTrapDetected(1); //probably could be set to the player #?
|
||||
}
|
||||
}
|
||||
|
||||
bool Highlightable::PossibleToSeeTrap() const
|
||||
{
|
||||
return CanDetectTrap();
|
||||
}
|
||||
|
||||
bool InfoPoint::PossibleToSeeTrap() const
|
||||
{
|
||||
// Only detectable trap-type infopoints.
|
||||
return (CanDetectTrap() && (Type == ST_PROXIMITY) );
|
||||
}
|
||||
|
||||
bool InfoPoint::CanDetectTrap() const
|
||||
{
|
||||
// Traps can be detected on all types of infopoint, as long
|
||||
// as the trap is detectable and isn't deactivated.
|
||||
return ((Flags&TRAP_DETECTABLE) && !(Flags&TRAP_DEACTIVATED));
|
||||
}
|
||||
|
||||
// returns true if the infopoint is a PS:T portal
|
||||
// GF_REVERSE_DOOR is the closest game feature (exists only in PST, and about area objects)
|
||||
bool InfoPoint::IsPortal() const
|
||||
{
|
||||
if (Type!=ST_TRAVEL) return false;
|
||||
if (Cursor != IE_CURSOR_PORTAL) return false;
|
||||
return core->HasFeature(GF_REVERSE_DOOR);
|
||||
}
|
||||
|
||||
//trap that is visible on screen (marked by red)
|
||||
//if TrapDetected is a bitflag, we could show traps selectively for
|
||||
//players, really nice for multiplayer
|
||||
bool Highlightable::VisibleTrap(int see_all) const
|
||||
{
|
||||
if (!Trapped) return false;
|
||||
if (!PossibleToSeeTrap()) return false;
|
||||
if (!Scripts[0]) return false;
|
||||
if (see_all) return true;
|
||||
if (TrapDetected ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//trap that will fire now
|
||||
bool Highlightable::TriggerTrap(int /*skill*/, ieDword ID)
|
||||
{
|
||||
if (!Trapped) {
|
||||
return false;
|
||||
}
|
||||
//actually this could be script name[0]
|
||||
if (!Scripts[0] && !EnterWav[0]) {
|
||||
return false;
|
||||
}
|
||||
LastTriggerObject = LastTrigger = LastEntered = ID;
|
||||
ImmediateEvent();
|
||||
if (!TrapResets()) {
|
||||
Trapped = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//trap that will fire now
|
||||
bool InfoPoint::TriggerTrap(int skill, ieDword ID)
|
||||
{
|
||||
if (Type!=ST_PROXIMITY) {
|
||||
return true;
|
||||
}
|
||||
if (Flags&TRAP_DEACTIVATED) {
|
||||
return false;
|
||||
}
|
||||
if (!Trapped) {
|
||||
// we have to set Entered somewhere, here seems best..
|
||||
LastEntered = ID;
|
||||
return true;
|
||||
} else if (Highlightable::TriggerTrap(skill, ID)) {
|
||||
if (!Trapped) {
|
||||
Flags|=TRAP_DEACTIVATED;
|
||||
}
|
||||
// ok, so this is a pain. Entered() trigger checks Trapped,
|
||||
// so it needs to be kept set. how to do this right?
|
||||
Trapped = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InfoPoint::Entered(Actor *actor)
|
||||
{
|
||||
if (outline->PointIn( actor->Pos ) ) {
|
||||
goto check;
|
||||
}
|
||||
// why is this here? actors which aren't *in* a trap get IF_INTRAP
|
||||
// repeatedly unset, so this triggers again and again and again.
|
||||
// i disabled it for ST_PROXIMITY for now..
|
||||
/*if (Type != ST_PROXIMITY && (PersonalDistance(Pos, actor)<MAX_OPERATING_DISTANCE) ) {
|
||||
goto check;
|
||||
}*/
|
||||
// this method is better (fuzzie, 2009) and also works for the iwd ar6002 northeast exit
|
||||
if (Type == ST_TRAVEL && PersonalDistance(TrapLaunch, actor)<MAX_OPERATING_DISTANCE) {
|
||||
goto check;
|
||||
}
|
||||
// fuzzie can't escape pst's ar1405 without this one, maybe we should really be checking
|
||||
// for distance from the outline for travel regions instead?
|
||||
if (Type == ST_TRAVEL && PersonalDistance(TalkPos, actor)<MAX_OPERATING_DISTANCE) {
|
||||
goto check;
|
||||
}
|
||||
if (Flags&TRAP_USEPOINT) {
|
||||
if (PersonalDistance(UsePoint, actor)<MAX_OPERATING_DISTANCE) {
|
||||
goto check;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
check:
|
||||
if (Type==ST_TRAVEL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actor->GetInternalFlag()&IF_INTRAP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor->InParty || (Flags&TRAP_NPC) ) {
|
||||
//no need to avoid a travel trigger
|
||||
|
||||
//skill?
|
||||
if (TriggerTrap(0, actor->GetGlobalID()) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoPoint::DebugDump() const
|
||||
{
|
||||
switch (Type) {
|
||||
case ST_TRIGGER:
|
||||
printf( "Debugdump of InfoPoint Region %s:\n", GetScriptName() );
|
||||
break;
|
||||
case ST_PROXIMITY:
|
||||
printf( "Debugdump of Trap Region %s:\n", GetScriptName() );
|
||||
break;
|
||||
case ST_TRAVEL:
|
||||
printf( "Debugdump of Travel Region %s:\n", GetScriptName() );
|
||||
break;
|
||||
default:
|
||||
printf( "Debugdump of Unsupported Region %s:\n", GetScriptName() );
|
||||
break;
|
||||
}
|
||||
printf( "Region Global ID: %d\n", GetGlobalID());
|
||||
printf( "Position: %d.%d\n", Pos.x, Pos.y);
|
||||
switch(Type) {
|
||||
case ST_TRAVEL:
|
||||
printf( "Destination Area: %s Entrance: %s\n", Destination, EntranceName);
|
||||
break;
|
||||
case ST_PROXIMITY:
|
||||
printf( "TrapDetected: %d, Trapped: %s\n", TrapDetected, YESNO(Trapped));
|
||||
printf( "Trap detection: %d%%, Trap removal: %d%%\n", TrapDetectionDiff,
|
||||
TrapRemovalDiff );
|
||||
break;
|
||||
case ST_TRIGGER:
|
||||
printf ( "InfoString: %s\n", overHeadText );
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
const char *name = "NONE";
|
||||
if (Scripts[0]) {
|
||||
name = Scripts[0]->GetName();
|
||||
}
|
||||
printf( "Script: %s, Key: %s, Dialog: %s\n", name, KeyResRef, Dialog );
|
||||
printf( "Active: %s\n", YESNO(InternalFlags&IF_ACTIVE));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INFOPOINT_H
|
||||
#define INFOPOINT_H
|
||||
|
||||
#include "Scriptable.h"
|
||||
|
||||
//trigger flags
|
||||
#define TRAP_INVISIBLE 1
|
||||
#define TRAP_RESET 2
|
||||
#define TRAVEL_PARTY 4
|
||||
#define TRAP_DETECTABLE 8
|
||||
//#define TRAP_16 16
|
||||
#define TRAP_LOWMEM 32 //special treatment when low on memory ?
|
||||
#define TRAP_NPC 64
|
||||
//#define TRAP_128 128
|
||||
#define TRAP_DEACTIVATED 256
|
||||
#define TRAVEL_NONPC 512
|
||||
#define TRAP_USEPOINT 1024 //override usage point of travel regions (used for sound in PST traps)
|
||||
#define INFO_DOOR 2048 //info trigger blocked by door
|
||||
|
||||
class GEM_EXPORT InfoPoint : public Highlightable {
|
||||
public:
|
||||
InfoPoint(void);
|
||||
~InfoPoint(void);
|
||||
//returns true if trap has been triggered, tumble skill???
|
||||
void SetEnter(const char *resref);
|
||||
bool TriggerTrap(int skill, ieDword ID);
|
||||
//call this to check if an actor entered the trigger zone
|
||||
bool Entered(Actor *actor);
|
||||
//checks if the actor may use this travel trigger
|
||||
int CheckTravel(Actor *actor);
|
||||
void DebugDump() const;
|
||||
int TrapResets() const { return Flags & TRAP_RESET; }
|
||||
bool CanDetectTrap() const;
|
||||
bool PossibleToSeeTrap() const;
|
||||
bool IsPortal() const;
|
||||
|
||||
public:
|
||||
ieResRef Destination;
|
||||
ieVariable EntranceName;
|
||||
ieDword Flags;
|
||||
//overheadtext contains the string, but we have to save this
|
||||
ieStrRef StrRef;
|
||||
Point UsePoint;
|
||||
Point TalkPos;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
1973
project/jni/application/gemrb/gemrb/core/Scriptable/Scriptable.cpp
Normal file
1973
project/jni/application/gemrb/gemrb/core/Scriptable/Scriptable.cpp
Normal file
File diff suppressed because it is too large
Load Diff
451
project/jni/application/gemrb/gemrb/core/Scriptable/Scriptable.h
Normal file
451
project/jni/application/gemrb/gemrb/core/Scriptable/Scriptable.h
Normal file
@@ -0,0 +1,451 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2003 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCRIPTABLE_H
|
||||
#define SCRIPTABLE_H
|
||||
|
||||
#include "exports.h"
|
||||
|
||||
#include "CharAnimations.h"
|
||||
#include "Inventory.h"
|
||||
#include "PathFinder.h"
|
||||
#include "Sprite2D.h"
|
||||
#include "TileOverlay.h"
|
||||
#include "Variables.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
class Action;
|
||||
class Actor;
|
||||
class Container;
|
||||
class Door;
|
||||
class GameScript;
|
||||
class Gem_Polygon;
|
||||
class Highlightable;
|
||||
class InfoPoint;
|
||||
class Movable;
|
||||
class Scriptable;
|
||||
class Selectable;
|
||||
class Spell;
|
||||
class SpriteCover;
|
||||
|
||||
#define MAX_SCRIPTS 8
|
||||
#define MAX_GROUND_ICON_DRAWN 3
|
||||
#define MAX_TIMER 256
|
||||
|
||||
/** The distance of operating a trigger, container, etc. */
|
||||
#define MAX_OPERATING_DISTANCE 40 //a search square is 16x12
|
||||
/** The distance between PC's who are about to enter a new area */
|
||||
#define MAX_TRAVELING_DISTANCE 400
|
||||
|
||||
#define SCR_OVERRIDE 0
|
||||
#define SCR_AREA 1
|
||||
#define SCR_SPECIFICS 2
|
||||
#define SCR_RESERVED 3
|
||||
#define SCR_CLASS 4
|
||||
#define SCR_RACE 5
|
||||
#define SCR_GENERAL 6
|
||||
#define SCR_DEFAULT 7
|
||||
|
||||
//pst trap flags (portal)
|
||||
#define PORTAL_CURSOR 1
|
||||
#define PORTAL_TRAVEL 2
|
||||
|
||||
//trigger flags
|
||||
#define TRAP_INVISIBLE 1
|
||||
#define TRAP_RESET 2
|
||||
#define TRAVEL_PARTY 4
|
||||
#define TRAP_DETECTABLE 8
|
||||
//#define TRAP_16 16
|
||||
#define TRAP_LOWMEM 32 //special treatment when low on memory ?
|
||||
#define TRAP_NPC 64
|
||||
//#define TRAP_128 128
|
||||
#define TRAP_DEACTIVATED 256
|
||||
#define TRAVEL_NONPC 512
|
||||
#define TRAP_USEPOINT 1024 //override usage point of travel regions (used for sound in PST traps)
|
||||
#define INFO_DOOR 2048 //info trigger blocked by door
|
||||
|
||||
//internal actor flags
|
||||
#define IF_GIVEXP 1 //give xp for this death
|
||||
#define IF_JUSTDIED 2 //Died() will return true
|
||||
#define IF_FROMGAME 4 //this is an NPC or PC
|
||||
#define IF_REALLYDIED 8 //real death happened, actor will be set to dead
|
||||
#define IF_NORECTICLE 16 //draw recticle (target mark)
|
||||
#define IF_NOINT 32 //cannot interrupt the actions of this actor (save is not possible!)
|
||||
#define IF_CLEANUP 64 //actor died chunky death, or other total destruction
|
||||
#define IF_RUNNING 128 //actor is running
|
||||
//these bits could be set by a WalkTo
|
||||
#define IF_RUNFLAGS (IF_RUNNING|IF_NORECTICLE|IF_NOINT)
|
||||
#define IF_BECAMEVISIBLE 0x100//actor just became visible (trigger event)
|
||||
#define IF_INITIALIZED 0x200
|
||||
#define IF_USEDSAVE 0x400 //actor needed saving throws
|
||||
#define IF_TARGETGONE 0x800 //actor's target is gone (trigger event)
|
||||
#define IF_USEEXIT 0x1000 //
|
||||
#define IF_INTRAP 0x2000 //actor is currently in a trap (intrap trigger event)
|
||||
#define IF_WASINDIALOG 0x4000 //actor just left dialog
|
||||
|
||||
//scriptable flags
|
||||
#define IF_ACTIVE 0x10000
|
||||
#define IF_VISIBLE 0x40000
|
||||
#define IF_ONCREATION 0x80000
|
||||
#define IF_IDLE 0x100000
|
||||
#define IF_PARTYRESTED 0x200000 //party rested trigger event
|
||||
|
||||
//the actor should stop attacking
|
||||
#define IF_STOPATTACK (IF_JUSTDIED|IF_REALLYDIED|IF_CLEANUP|IF_IDLE)
|
||||
|
||||
//CheckTravel return value
|
||||
#define CT_CANTMOVE 0 //inactive
|
||||
#define CT_ACTIVE 1 //actor can move
|
||||
#define CT_GO_CLOSER 2 //entire team would move, but not close enough
|
||||
#define CT_WHOLE 3 //team can move
|
||||
#define CT_SELECTED 4 //not all selected actors are there
|
||||
#define CT_MOVE_SELECTED 5 //all selected can move
|
||||
|
||||
//bits for binary trigger bitfield
|
||||
#define BT_DIE 1
|
||||
#define BT_ONCREATION 2
|
||||
#define BT_BECAMEVISIBLE 4
|
||||
#define BT_WASINDIALOG 8
|
||||
#define BT_PARTYRESTED 16
|
||||
#define BT_VACANT 32
|
||||
|
||||
//xp bonus types (for xpbonus.2da)
|
||||
#define XP_LOCKPICK 0
|
||||
#define XP_DISARM 1
|
||||
#define XP_LEARNSPELL 2
|
||||
|
||||
typedef enum ScriptableType { ST_ACTOR = 0, ST_PROXIMITY = 1, ST_TRIGGER = 2,
|
||||
ST_TRAVEL = 3, ST_DOOR = 4, ST_CONTAINER = 5, ST_AREA = 6, ST_GLOBAL = 7 } ScriptableType;
|
||||
|
||||
typedef std::list<ieDword *> TriggerObjects;
|
||||
|
||||
//#define SEA_RESET 0x00000002
|
||||
//#define SEA_PARTY_REQUIRED 0x00000004
|
||||
|
||||
class GEM_EXPORT Scriptable {
|
||||
public:
|
||||
Scriptable(ScriptableType type);
|
||||
virtual ~Scriptable(void);
|
||||
private:
|
||||
TriggerObjects tolist;
|
||||
ieDword bittriggers;
|
||||
unsigned long startTime;
|
||||
unsigned long interval;
|
||||
unsigned long WaitCounter;
|
||||
// script_timers should probably be a std::map to
|
||||
// conserve memory (usually at most 2 ids are used)
|
||||
ieDword script_timers[MAX_TIMER];
|
||||
ieDword globalID;
|
||||
protected: //let Actor access this
|
||||
Map *area;
|
||||
ieVariable scriptName;
|
||||
ieDword InternalFlags; //for triggers
|
||||
ieResRef Dialog;
|
||||
std::list< Action*> actionQueue;
|
||||
Action* CurrentAction;
|
||||
public:
|
||||
// State relating to the currently-running action.
|
||||
int CurrentActionState;
|
||||
ieDword CurrentActionTarget;
|
||||
bool CurrentActionInterruptable;
|
||||
|
||||
// Timing state for ExecuteScript.
|
||||
ieDword lastDelay;
|
||||
ieDword lastRunTime;
|
||||
|
||||
Variables* locals;
|
||||
ScriptableType Type;
|
||||
Point Pos;
|
||||
|
||||
ieStrRef DialogName;
|
||||
//play this wav file when stepping on the trap
|
||||
ieResRef EnterWav;
|
||||
|
||||
GameScript* Scripts[MAX_SCRIPTS];
|
||||
|
||||
// Variables for overhead text.
|
||||
char* overHeadText;
|
||||
Point overHeadTextPos;
|
||||
unsigned char textDisplaying;
|
||||
unsigned long timeStartDisplaying;
|
||||
|
||||
ieDword UnselectableTimer;
|
||||
|
||||
// Stored objects.
|
||||
ieDword TriggerID; //for sendtrigger
|
||||
ieDword LastTrigger; // also LastClosed
|
||||
ieDword LastTriggerObject; // hack until someone fixes triggers
|
||||
ieDword LastEntered; // also LastOpened
|
||||
ieDword LastDisarmed; // also LastAttacker
|
||||
ieDword LastDisarmFailed; //also LastTarget
|
||||
ieDword LastUnlocked;
|
||||
ieDword LastOpenFailed; // also LastPickpocketFailed
|
||||
ieDword LastPickLockFailed;
|
||||
int LastOrder;
|
||||
ieDword LastOrderer;
|
||||
ieDword LastSpellOnMe; //Last spell cast on this scriptable
|
||||
ieDword LastCasterOnMe; //Last spellcaster on this scriptable
|
||||
ieDword LastSpellSeen; //Last spell seen to be cast
|
||||
ieDword LastCasterSeen; //Last spellcaster seen
|
||||
|
||||
Point LastTargetPos;
|
||||
int SpellHeader;
|
||||
ieResRef SpellResRef;
|
||||
public:
|
||||
/** Gets the Dialog ResRef */
|
||||
const char* GetDialog(void) const
|
||||
{
|
||||
return Dialog;
|
||||
}
|
||||
void SetDialog(const char *resref);
|
||||
void SetScript(const ieResRef aScript, int idx, bool ai=false);
|
||||
void SetSpellResRef(ieResRef resref);
|
||||
void SetWait(unsigned long time);
|
||||
unsigned long GetWait() const;
|
||||
void LeaveDialog();
|
||||
void Interrupt();
|
||||
void NoInterrupt();
|
||||
void Hide();
|
||||
void Unhide();
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
void PartyRested();
|
||||
ieDword GetInternalFlag();
|
||||
const char* GetScriptName() const;
|
||||
Map* GetCurrentArea() const;
|
||||
void SetMap(Map *map);
|
||||
void SetScript(int index, GameScript* script);
|
||||
void DisplayHeadText(const char* text);
|
||||
void FixHeadTextPos();
|
||||
void SetScriptName(const char* text);
|
||||
//call this to deny script running in the next AI cycle
|
||||
void DelayedEvent();
|
||||
//call this to enable script running as soon as possible
|
||||
void ImmediateEvent();
|
||||
bool IsPC() const;
|
||||
void ExecuteScript(int scriptCount);
|
||||
void AddAction(Action* aC);
|
||||
void AddActionInFront(Action* aC);
|
||||
Action* GetCurrentAction() const { return CurrentAction; }
|
||||
Action* GetNextAction() const;
|
||||
Action* PopNextAction();
|
||||
void ClearActions();
|
||||
void ReleaseCurrentAction();
|
||||
bool InMove() const;
|
||||
void ProcessActions(bool force);
|
||||
//these functions handle clearing of triggers that resulted a
|
||||
//true condition (whole triggerblock returned true)
|
||||
void InitTriggers();
|
||||
void ClearTriggers();
|
||||
void SetBitTrigger(ieDword bittrigger);
|
||||
void AddTrigger(ieDword *actorref);
|
||||
/* re/draws overhead text on the map screen */
|
||||
void DrawOverheadText(const Region &screen);
|
||||
/* check if casting is allowed at all */
|
||||
int CanCast(const ieResRef SpellResRef);
|
||||
/* check for and trigger a wild surge */
|
||||
int CheckWildSurge();
|
||||
/* actor/scriptable casts spell */
|
||||
int CastSpellPoint( ieResRef &SpellRef, const Point &Target, bool deplete, bool instant = false );
|
||||
int CastSpell( ieResRef &SpellRef, Scriptable* Target, bool deplete, bool instant = false );
|
||||
/* spellcasting finished */
|
||||
void CastSpellPointEnd(int level);
|
||||
void CastSpellEnd(int level);
|
||||
ieDword GetGlobalID() const { return globalID; }
|
||||
/** timer functions (numeric ID, not saved) */
|
||||
bool TimerActive(ieDword ID);
|
||||
bool TimerExpired(ieDword ID);
|
||||
void StartTimer(ieDword ID, ieDword expiration);
|
||||
virtual char* GetName(int /*which*/) const { return NULL; }
|
||||
private:
|
||||
/* used internally to handle start of spellcasting */
|
||||
int SpellCast(bool instant);
|
||||
/* also part of the spellcasting process, creating the projectile */
|
||||
void CreateProjectile(const ieResRef SpellResRef, ieDword tgt, int level, bool fake);
|
||||
/* do some magic for the wierd/awesome wild surges */
|
||||
bool HandleHardcodedSurge(ieResRef surgeSpellRef, Spell *spl, Actor *caster);
|
||||
};
|
||||
|
||||
class GEM_EXPORT Selectable : public Scriptable {
|
||||
public:
|
||||
Selectable(ScriptableType type);
|
||||
virtual ~Selectable(void);
|
||||
public:
|
||||
Region BBox;
|
||||
ieWord Selected; //could be 0x80 for unselectable
|
||||
bool Over;
|
||||
Color selectedColor;
|
||||
Color overColor;
|
||||
Sprite2D *circleBitmap[2];
|
||||
int size;
|
||||
private:
|
||||
// current SpriteCover for wallgroups
|
||||
SpriteCover* cover;
|
||||
public:
|
||||
void SetBBox(const Region &newBBox);
|
||||
void DrawCircle(const Region &vp);
|
||||
bool IsOver(const Point &Pos) const;
|
||||
void SetOver(bool over);
|
||||
bool IsSelected() const;
|
||||
void Select(int Value);
|
||||
void SetCircle(int size, const Color &color, Sprite2D* normal_circle, Sprite2D* selected_circle);
|
||||
|
||||
/* store SpriteCover */
|
||||
void SetSpriteCover(SpriteCover* c);
|
||||
/* get stored SpriteCover */
|
||||
SpriteCover* GetSpriteCover() const { return cover; }
|
||||
/* want dithered SpriteCover */
|
||||
int WantDither();
|
||||
};
|
||||
|
||||
class GEM_EXPORT Highlightable : public Scriptable {
|
||||
public:
|
||||
Highlightable(ScriptableType type);
|
||||
virtual ~Highlightable(void);
|
||||
virtual int TrapResets() const = 0;
|
||||
virtual bool CanDetectTrap() const { return true; }
|
||||
virtual bool PossibleToSeeTrap() const;
|
||||
public:
|
||||
Gem_Polygon* outline;
|
||||
Color outlineColor;
|
||||
ieDword Cursor;
|
||||
bool Highlight;
|
||||
Point TrapLaunch;
|
||||
ieWord TrapDetectionDiff;
|
||||
ieWord TrapRemovalDiff;
|
||||
ieWord Trapped;
|
||||
ieWord TrapDetected;
|
||||
ieResRef KeyResRef;
|
||||
public:
|
||||
bool IsOver(const Point &Pos) const;
|
||||
void DrawOutline() const;
|
||||
void SetCursor(unsigned char CursorIndex);
|
||||
const char* GetKey(void) const
|
||||
{
|
||||
if (KeyResRef[0]) return KeyResRef;
|
||||
return NULL;
|
||||
}
|
||||
void SetTrapDetected(int x);
|
||||
void TryDisarm(Actor *actor);
|
||||
//detect trap, set skill to 256 if you want sure fire
|
||||
void DetectTrap(int skill);
|
||||
//returns true if trap is visible, only_detected must be true
|
||||
//if you want to see discovered traps, false is for cheats
|
||||
bool VisibleTrap(int only_detected) const;
|
||||
//returns true if trap has been triggered, tumble skill???
|
||||
virtual bool TriggerTrap(int skill, ieDword ID);
|
||||
bool TryUnlock(Actor *actor, bool removekey);
|
||||
};
|
||||
|
||||
class GEM_EXPORT Movable : public Selectable {
|
||||
private: //these seem to be sensitive, so get protection
|
||||
unsigned char StanceID;
|
||||
unsigned char Orientation, NewOrientation;
|
||||
ieWord AttackMovements[3];
|
||||
|
||||
PathNode* path; //whole path
|
||||
PathNode* step; //actual step
|
||||
public:
|
||||
Movable(ScriptableType type);
|
||||
virtual ~Movable(void);
|
||||
Point Destination;
|
||||
ieDword timeStartStep;
|
||||
Sprite2D* lastFrame;
|
||||
ieResRef Area;
|
||||
public:
|
||||
PathNode *GetNextStep(int x);
|
||||
int GetPathLength();
|
||||
//inliners to protect data consistency
|
||||
inline PathNode * GetNextStep() {
|
||||
if (!step) {
|
||||
DoStep((unsigned int) ~0);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
inline unsigned char GetOrientation() const {
|
||||
return Orientation;
|
||||
}
|
||||
|
||||
inline unsigned char GetNextFace() {
|
||||
//slow turning
|
||||
if (Orientation != NewOrientation) {
|
||||
if ( ( (NewOrientation-Orientation) & (MAX_ORIENT-1) ) <= MAX_ORIENT/2) {
|
||||
Orientation++;
|
||||
} else {
|
||||
Orientation--;
|
||||
}
|
||||
Orientation = Orientation&(MAX_ORIENT-1);
|
||||
}
|
||||
|
||||
return Orientation;
|
||||
}
|
||||
inline unsigned char GetStance() const {
|
||||
return StanceID;
|
||||
}
|
||||
|
||||
inline void SetOrientation(int value, bool slow) {
|
||||
//MAX_ORIENT == 16, so we can do this
|
||||
NewOrientation = (unsigned char) (value&(MAX_ORIENT-1));
|
||||
if (!slow) {
|
||||
Orientation = NewOrientation;
|
||||
}
|
||||
}
|
||||
|
||||
void SetStance(unsigned int arg);
|
||||
void SetAttackMoveChances(ieWord *amc);
|
||||
bool DoStep(unsigned int walk_speed, ieDword time = 0);
|
||||
void AddWayPoint(const Point &Des);
|
||||
void RunAwayFrom(const Point &Des, int PathLength, int flags);
|
||||
void RandomWalk(bool can_stop, bool run);
|
||||
void MoveLine(int steps, int Pass, ieDword Orient);
|
||||
void FixPosition();
|
||||
void WalkTo(const Point &Des, int MinDistance = 0);
|
||||
void MoveTo(const Point &Des);
|
||||
void ClearPath();
|
||||
void DrawTargetPoint(const Region &vp);
|
||||
/* returns the most likely position of this actor */
|
||||
Point GetMostLikelyPosition();
|
||||
virtual bool BlocksSearchMap() const = 0;
|
||||
|
||||
};
|
||||
|
||||
//Tiled objects are not used (and maybe not even implemented correctly in IE)
|
||||
//they seem to be most closer to a door and probably obsoleted by it
|
||||
//are they scriptable?
|
||||
class GEM_EXPORT TileObject {
|
||||
public:
|
||||
TileObject(void);
|
||||
~TileObject(void);
|
||||
void SetOpenTiles(unsigned short *indices, int count);
|
||||
void SetClosedTiles(unsigned short *indices, int count);
|
||||
|
||||
public:
|
||||
ieVariable Name;
|
||||
ieResRef Tileset; //or wed door ID?
|
||||
ieDword Flags;
|
||||
unsigned short* opentiles;
|
||||
ieDword opencount;
|
||||
unsigned short* closedtiles;
|
||||
ieDword closedcount;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -40,6 +40,7 @@
|
||||
#define NINE 16 //nine faces (orientation)
|
||||
#define SEVENEYES 32 //special hack for seven eyes
|
||||
|
||||
#define DEFAULT_FRAMERATE 15
|
||||
#define MAX_CYCLE_TYPE 16
|
||||
static const ieByte ctypes[MAX_CYCLE_TYPE]={
|
||||
ILLEGAL, ONE, TWO, THREE, TWO|DOUBLE, ONE|FIVE, THREE|DOUBLE, ILLEGAL,
|
||||
@@ -75,7 +76,7 @@ void ScriptedAnimation::Init()
|
||||
Fade = 0;
|
||||
SequenceFlags = 0;
|
||||
XPos = YPos = ZPos = 0;
|
||||
FrameRate = 15;
|
||||
FrameRate = DEFAULT_FRAMERATE;
|
||||
FaceTarget = 0;
|
||||
Orientation = 0;
|
||||
Dither = 0;
|
||||
@@ -86,6 +87,12 @@ void ScriptedAnimation::Init()
|
||||
Phase = P_NOTINITED;
|
||||
effect_owned = false;
|
||||
active = true;
|
||||
Delay = 0;
|
||||
light = NULL;
|
||||
LightX = 0;
|
||||
LightY = 0;
|
||||
LightZ = 0;
|
||||
starttime = 0;
|
||||
}
|
||||
|
||||
void ScriptedAnimation::Override(ScriptedAnimation *templ)
|
||||
@@ -252,11 +259,16 @@ ScriptedAnimation::ScriptedAnimation(DataStream* stream, bool autoFree)
|
||||
ZPos = (signed) tmp;
|
||||
stream->Seek( 4, GEM_CURRENT_POS );
|
||||
stream->ReadDword( &FrameRate );
|
||||
|
||||
if (!FrameRate) FrameRate = DEFAULT_FRAMERATE;
|
||||
|
||||
stream->ReadDword( &FaceTarget );
|
||||
stream->Seek( 16, GEM_CURRENT_POS );
|
||||
stream->ReadDword( &tmp ); //this doesn't affect visibility
|
||||
YPos = (signed) tmp;
|
||||
stream->Seek( 12, GEM_CURRENT_POS );
|
||||
stream->ReadDword( &LightX );
|
||||
stream->ReadDword( &LightY );
|
||||
stream->ReadDword( &LightZ );
|
||||
stream->ReadDword( &Duration );
|
||||
stream->Seek( 8, GEM_CURRENT_POS );
|
||||
stream->ReadDword( &seq1 );
|
||||
@@ -395,6 +407,9 @@ ScriptedAnimation::~ScriptedAnimation(void)
|
||||
sound_handle->Stop();
|
||||
sound_handle.release();
|
||||
}
|
||||
if(light) {
|
||||
core->GetVideoDriver()->FreeSprite(light);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedAnimation::SetPhase(int arg)
|
||||
@@ -490,8 +505,17 @@ ieDword ScriptedAnimation::GetSequenceDuration(ieDword multiplier)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptedAnimation::SetDelay(ieDword delay)
|
||||
{
|
||||
Delay = delay;
|
||||
if (twin) {
|
||||
twin->Delay=delay;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedAnimation::SetDefaultDuration(ieDword duration)
|
||||
{
|
||||
if (!(SequenceFlags&(IE_VVC_LOOP|IE_VVC_FREEZE) )) return;
|
||||
if (Duration==0xffffffff) {
|
||||
Duration = duration;
|
||||
}
|
||||
@@ -517,11 +541,32 @@ void ScriptedAnimation::SetOrientation(int orientation)
|
||||
|
||||
bool ScriptedAnimation::HandlePhase(Sprite2D *&frame)
|
||||
{
|
||||
unsigned int inc = 0;
|
||||
|
||||
if (justCreated) {
|
||||
if (Phase == P_NOTINITED) {
|
||||
printMessage("ScriptedAnimation", "Not fully initialised VVC!\n", LIGHT_RED);
|
||||
return true;
|
||||
}
|
||||
unsigned long time;
|
||||
time = core->GetGame()->Ticks;
|
||||
if (starttime == 0) {
|
||||
starttime = time;
|
||||
}
|
||||
if (( time - starttime ) >= ( unsigned long ) ( 1000 / FrameRate )) {
|
||||
inc = (time-starttime)*FrameRate/1000;
|
||||
starttime += inc*1000/FrameRate;
|
||||
}
|
||||
|
||||
if (Delay>inc) {
|
||||
Delay-=inc;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SequenceFlags&IE_VVC_LIGHTSPOT) {
|
||||
light = core->GetVideoDriver()->CreateLight(LightX, LightZ);
|
||||
}
|
||||
|
||||
if (Duration!=0xffffffff) {
|
||||
Duration += core->GetGame()->GameTime;
|
||||
}
|
||||
@@ -558,6 +603,10 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
if (SequenceFlags&IE_VVC_FREEZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//automatically slip from onset to hold to release
|
||||
if (!frame || anims[Phase*MAX_ORIENT+Orientation]->endReached) {
|
||||
if (Phase>=P_RELEASE) {
|
||||
@@ -594,9 +643,15 @@ bool ScriptedAnimation::Draw(const Region &screen, const Point &Pos, const Color
|
||||
Sprite2D* frame;
|
||||
|
||||
if (HandlePhase(frame)) {
|
||||
//expired
|
||||
return true;
|
||||
}
|
||||
|
||||
//delayed
|
||||
if (justCreated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ieDword flag = BLIT_TRANSSHADOW;
|
||||
//transferring flags to SDLdriver, this will have to be consolidated later
|
||||
|
||||
@@ -614,7 +669,7 @@ bool ScriptedAnimation::Draw(const Region &screen, const Point &Pos, const Color
|
||||
flag |= BLIT_GREY;
|
||||
}
|
||||
|
||||
if (Transparency & IE_VVC_RED_TINT) {
|
||||
if (Transparency & IE_VVC_SEPIA) {
|
||||
flag |= BLIT_RED;
|
||||
}
|
||||
|
||||
@@ -642,6 +697,9 @@ bool ScriptedAnimation::Draw(const Region &screen, const Point &Pos, const Color
|
||||
}
|
||||
|
||||
video->BlitGameSprite( frame, cx + screen.x, cy + screen.y, flag, tint, cover, palette, &screen);
|
||||
if (light) {
|
||||
video->BlitGameSprite( light, cx + screen.x, cy + screen.y, 0, tint, NULL, NULL, &screen);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,17 +36,31 @@
|
||||
#define IE_VVC_BLENDED 0x00000008
|
||||
#define IE_VVC_MIRRORX 0x00000010
|
||||
#define IE_VVC_MIRRORY 0x00000020
|
||||
#define IE_VVC_CLIPPED 0x00000040
|
||||
#define IE_VVC_3D_BLEND 0x00000200
|
||||
#define IE_VVC_NOCOVER_2 0x00000400
|
||||
#define IE_VVC_NO_TIMESTOP 0x00000800 //ignore timestop palette
|
||||
#define IE_VVC_NO_SEPIA 0x00001000 //ignore dream palette
|
||||
#define IE_VVC_2D_BLEND 0x00002000
|
||||
#define IE_VVC_TINT 0x00030000 //2 bits need to be set for tint
|
||||
#define IE_VVC_GREYSCALE 0x00080000
|
||||
#define IE_VVC_GREYSCALE 0x00080000 //timestopped palette
|
||||
#define IE_VVC_DARKEN 0x00100000 //this is unsure
|
||||
#define IE_VVC_GLOWING 0x00200000
|
||||
#define IE_VVC_RED_TINT 0x02000000
|
||||
#define IE_VVC_GLOWING 0x00200000 //internal gamma
|
||||
#define IE_VVC_SEPIA 0x02000000 //dream palette
|
||||
|
||||
#define IE_VVC_LOOP 0x00000001
|
||||
#define IE_VVC_LIGHTSPOT 0x00000002 //draw lightspot
|
||||
#define IE_VVC_HEIGHT 0x00000004
|
||||
#define IE_VVC_BAM 0x00000008
|
||||
#define IE_VVC_OWN_PAL 0x00000010
|
||||
#define IE_VVC_NOCOVER 0x00000040
|
||||
#define IE_VVC_MID_BRIGHTEN 0x00000080
|
||||
#define IE_VVC_HIGH_BRIGHTEN 0x00000100
|
||||
|
||||
#define IE_VVC_UNUSED 0xe0000000U
|
||||
|
||||
//#define IE_VVC_UNUSED 0xe0000000U
|
||||
//gemrb specific sequence flags
|
||||
#define IE_VVC_FREEZE 0x80000000
|
||||
|
||||
//phases
|
||||
#define P_NOTINITED -1
|
||||
@@ -78,10 +92,13 @@ public:
|
||||
int Dither;
|
||||
//these are signed
|
||||
int XPos, YPos, ZPos;
|
||||
ieDword LightX, LightY, LightZ;
|
||||
Sprite2D* light;//this is just a round/halftrans sprite, has no animation
|
||||
ieDword FrameRate;
|
||||
ieDword FaceTarget;
|
||||
ieByte Orientation;
|
||||
ieDword Duration;
|
||||
ieDword Delay;
|
||||
bool justCreated;
|
||||
ieResRef ResName;
|
||||
int Phase;
|
||||
@@ -90,6 +107,7 @@ public:
|
||||
bool active;
|
||||
bool effect_owned;
|
||||
Holder<SoundHandle> sound_handle;
|
||||
unsigned long starttime;
|
||||
public:
|
||||
//draws the next frame of the videocell
|
||||
bool Draw(const Region &screen, const Point &Pos, const Color &tint, Map *area, int dither, int orientation);
|
||||
@@ -111,6 +129,8 @@ public:
|
||||
SpriteCover* GetSpriteCover() const { return cover; }
|
||||
int GetCurrentFrame();
|
||||
ieDword GetSequenceDuration(ieDword multiplier);
|
||||
/* sets up a delay in the beginning of the vvc */
|
||||
void SetDelay(ieDword delay);
|
||||
/* sets default duration if it wasn't set yet */
|
||||
void SetDefaultDuration(unsigned int duration);
|
||||
/* sets up the direction of the vvc */
|
||||
|
||||
@@ -73,7 +73,7 @@ int Spell::GetHeaderIndexFromLevel(int level) const
|
||||
//-1 will return cfb
|
||||
//0 will always return first spell block
|
||||
//otherwise set to caster level
|
||||
static EffectRef fx_casting_glow_ref={"CastingGlow",NULL,-1};
|
||||
static EffectRef fx_casting_glow_ref = { "CastingGlow", -1 };
|
||||
|
||||
void Spell::AddCastingGlow(EffectQueue *fxqueue, ieDword duration, int gender)
|
||||
{
|
||||
@@ -118,7 +118,7 @@ void Spell::AddCastingGlow(EffectQueue *fxqueue, ieDword duration, int gender)
|
||||
delete fx;
|
||||
}
|
||||
|
||||
EffectQueue *Spell::GetEffectBlock(Scriptable *self, const Point &pos, int block_index, ieDword pro) const
|
||||
EffectQueue *Spell::GetEffectBlock(Scriptable *self, const Point &pos, int block_index, int level, ieDword pro) const
|
||||
{
|
||||
Effect *features;
|
||||
int count;
|
||||
@@ -140,21 +140,23 @@ EffectQueue *Spell::GetEffectBlock(Scriptable *self, const Point &pos, int block
|
||||
EffectQueue *selfqueue = NULL;
|
||||
|
||||
for (int i=0;i<count;i++) {
|
||||
if (Flags & SF_SIMPLIFIED_DURATION) {
|
||||
Effect *fx = features+i;
|
||||
|
||||
if ((Flags & SF_SIMPLIFIED_DURATION) && (block_index>=0)) {
|
||||
//hack the effect according to Level
|
||||
//fxqueue->AddEffect will copy the effect,
|
||||
//so we don't risk any overwriting
|
||||
if (EffectQueue::HasDuration(features+i)) {
|
||||
features[i].Duration = (TimePerLevel*block_index+TimeConstant)*core->Time.round_sec;
|
||||
fx->Duration = (TimePerLevel*block_index+TimeConstant)*core->Time.round_sec;
|
||||
}
|
||||
}
|
||||
//fill these for completeness, inventoryslot is a good way
|
||||
//to discern a spell from an item effect
|
||||
Effect *fx = features+i;
|
||||
|
||||
fx->InventorySlot = 0xffff;
|
||||
//the hostile flag is used to determine if this was an attack
|
||||
fx->SourceFlags = Flags;
|
||||
fx->CasterLevel = level;
|
||||
|
||||
// apply the stat-based spell duration modifier
|
||||
if (self->Type == ST_ACTOR) {
|
||||
|
||||
@@ -57,6 +57,9 @@ class Projectile;
|
||||
//this is not the same as the book types which is 3 or 11)
|
||||
#define NUM_SPELL_TYPES 6
|
||||
|
||||
#define SPEC_IDENTIFY 1 //spells that don't appear in the casting bar
|
||||
#define SPEC_SILENCE 2 //spells that can be cast when silenced
|
||||
#define SPEC_DEAD 4 //spells that can target dead actors despite their target type is 1 (pst hack)
|
||||
/**
|
||||
* @class SPLExtHeader
|
||||
* Header for Spell special effects
|
||||
@@ -160,7 +163,7 @@ public:
|
||||
//converts a wanted level to block index count
|
||||
int GetHeaderIndexFromLevel(int level) const;
|
||||
//-1 will return the cfb
|
||||
EffectQueue *GetEffectBlock(Scriptable *self, const Point &pos, int block_index, ieDword pro=0) const;
|
||||
EffectQueue *GetEffectBlock(Scriptable *self, const Point &pos, int block_index, int level, ieDword pro=0) const;
|
||||
// add appropriate casting glow effect
|
||||
void AddCastingGlow(EffectQueue *fxqueue, ieDword duration, int gender);
|
||||
//returns a projectile created from an extended header
|
||||
|
||||
@@ -47,6 +47,7 @@ class Spell;
|
||||
#define LS_LEARN 2 //give message when learned it
|
||||
#define LS_STATS 4 //check stats (alignment, etc)
|
||||
#define LS_MEMO 8 //memorize it instantly (add innate)
|
||||
#define LS_NOXP 16 //disable giving of xp (LS_ADDXP)
|
||||
|
||||
//LearnSpell return values
|
||||
#define LSR_OK 0
|
||||
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
ieDword PurchasedCategoriesOffset;
|
||||
ieDword PurchasedCategoriesCount;
|
||||
ieDword ItemsOffset;
|
||||
//don't use this value directly, use GetRealStockSize
|
||||
//don't use this value directly, use GetRealStockSize
|
||||
ieDword ItemsCount;
|
||||
ieDword Lore;
|
||||
ieDword IDPrice;
|
||||
|
||||
@@ -131,10 +131,10 @@ GEM_EXPORT void FixPath (char *path, bool needslash);
|
||||
|
||||
GEM_EXPORT void ExtractFileFromPath(char *file, const char *full_path);
|
||||
|
||||
class DirectoryIterator {
|
||||
class GEM_EXPORT DirectoryIterator {
|
||||
public:
|
||||
/**
|
||||
* @param[in] path Path to direcrtory to search.
|
||||
* @param[in] path Path to directory to search.
|
||||
*
|
||||
* WARNING: the lifetime of path must be longer than the lifetime
|
||||
* of DirectoryIterator.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2011 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <android/log.h>
|
||||
|
||||
int android_log_printf(const char * fmt, ...) {
|
||||
int return_value;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int characters = vfprintf(stdout, fmt, ap); // determine buffer size
|
||||
if(characters<0) return characters;
|
||||
char* buff = new char[characters+1];
|
||||
return_value = vsprintf(buff, fmt, ap);
|
||||
va_end(ap);
|
||||
__android_log_print(ANDROID_LOG_INFO, "printf:", buff);
|
||||
delete buff;
|
||||
return return_value;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* GemRB - Infinity Engine Emulator
|
||||
* Copyright (C) 2011 The GemRB Project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef IE_ANDROID_LOG_PRINTF_H
|
||||
#define IE_ANDROID_LOG_PRINTF_H
|
||||
|
||||
int android_log_printf(const char * fmt, ...);
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,10 @@
|
||||
#include "Interface.h"
|
||||
#include "Video.h"
|
||||
|
||||
#include "Scriptable/Container.h"
|
||||
#include "Scriptable/Door.h"
|
||||
#include "Scriptable/InfoPoint.h"
|
||||
|
||||
TileMap::TileMap(void)
|
||||
{
|
||||
XCellCount = 0;
|
||||
|
||||
@@ -25,11 +25,15 @@
|
||||
|
||||
#include "Polygon.h"
|
||||
#include "TileOverlay.h"
|
||||
#include "GameScript/GameScript.h"
|
||||
|
||||
//special container types
|
||||
#define IE_CONTAINER_PILE 4
|
||||
|
||||
class Container;
|
||||
class Door;
|
||||
class InfoPoint;
|
||||
class TileObject;
|
||||
|
||||
class GEM_EXPORT TileMap {
|
||||
private:
|
||||
std::vector< TileOverlay*> overlays;
|
||||
|
||||
@@ -418,13 +418,19 @@ void Variables::SetAt(const char* key, void* value)
|
||||
}
|
||||
|
||||
|
||||
void Variables::SetAt(const char* key, ieDword value)
|
||||
void Variables::SetAt(const char* key, ieDword value, bool nocreate)
|
||||
{
|
||||
unsigned int nHash;
|
||||
Variables::MyAssoc* pAssoc;
|
||||
|
||||
assert( m_type == GEM_VARIABLES_INT );
|
||||
if (( pAssoc = GetAssocAt( key, nHash ) ) == NULL) {
|
||||
if (nocreate) {
|
||||
printMessage("Variables", " ", YELLOW);
|
||||
printf("Cannot create new variable: %s\n", key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pHashTable == NULL)
|
||||
InitHashTable( m_nHashTableSize );
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
void SetAtCopy(const char* key, int newValue);
|
||||
void SetAt(const char* key, char* newValue);
|
||||
void SetAt(const char* key, void* newValue);
|
||||
void SetAt(const char* key, ieDword newValue);
|
||||
void SetAt(const char* key, ieDword newValue, bool nocreate=false);
|
||||
void Remove(const char* key);
|
||||
void RemoveAll(ReleaseFun fun);
|
||||
void InitHashTable(unsigned int hashSize, bool bAllocNow = true);
|
||||
|
||||
@@ -143,6 +143,8 @@ Sprite2D* Video::SpriteScaleDown( const Sprite2D* sprite, unsigned int ratio )
|
||||
return small;
|
||||
}
|
||||
|
||||
//TODO light could be elliptical in the original engine
|
||||
//is it difficult?
|
||||
Sprite2D* Video::CreateLight(int radius, int intensity)
|
||||
{
|
||||
if(!radius) return NULL;
|
||||
|
||||
@@ -49,8 +49,8 @@ enum SpriteBlitFlags {
|
||||
BLIT_NOSHADOW = 0x1000,
|
||||
BLIT_TRANSSHADOW = 0x2000,
|
||||
BLIT_TINTED = 0x00010000, // IE_VVC_TINT = 0x00030000
|
||||
BLIT_GREY = IE_VVC_GREYSCALE, // 0x80000
|
||||
BLIT_RED = IE_VVC_RED_TINT, // 0x02000000
|
||||
BLIT_GREY = IE_VVC_GREYSCALE, // 0x80000; timestop palette
|
||||
BLIT_RED = IE_VVC_SEPIA, // 0x02000000; dream scene palette
|
||||
BLIT_DARK = IE_VVC_DARKEN, // 0x00100000; not implemented in SDLVideo yet
|
||||
BLIT_GLOW = IE_VVC_GLOWING // 0x00200000; not implemented in SDLVideo yet
|
||||
// Note: bits 29,30,31 are used by SDLVideo internally
|
||||
|
||||
@@ -171,21 +171,21 @@ void WorldMap::SetAreaEntry(unsigned int x, WMPAreaEntry *ae)
|
||||
area_entries.push_back(ae);
|
||||
}
|
||||
|
||||
void WorldMap::InsertAreaLink(unsigned int idx, unsigned int dir, WMPAreaLink *arealink)
|
||||
void WorldMap::InsertAreaLink(unsigned int areaidx, unsigned int dir, WMPAreaLink *arealink)
|
||||
{
|
||||
unsigned int pos;
|
||||
WMPAreaEntry *ae;
|
||||
|
||||
WMPAreaLink *al = new WMPAreaLink();
|
||||
memcpy(al, arealink, sizeof(WMPAreaLink) );
|
||||
unsigned int max = area_links.size();
|
||||
unsigned int idx = area_entries[areaidx]->AreaLinksIndex[dir];
|
||||
area_links.insert(area_links.begin()+idx,al);
|
||||
|
||||
max = area_entries.size();
|
||||
unsigned int max = area_entries.size();
|
||||
for(pos = 0; pos<max; pos++) {
|
||||
ae = area_entries[pos];
|
||||
for (unsigned int k=0;k<4;k++) {
|
||||
if ((pos==idx) && (k==dir)) {
|
||||
if ((pos==areaidx) && (k==dir)) {
|
||||
ae->AreaLinksCount[k]++;
|
||||
continue;
|
||||
}
|
||||
|
||||
1
project/jni/application/gemrb/gemrb/docs/CMakeLists.txt
Normal file
1
project/jni/application/gemrb/gemrb/docs/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
ADD_SUBDIRECTORY( en )
|
||||
1
project/jni/application/gemrb/gemrb/docs/Makefile.am
Normal file
1
project/jni/application/gemrb/gemrb/docs/Makefile.am
Normal file
@@ -0,0 +1 @@
|
||||
SUBDIRS = en
|
||||
@@ -0,0 +1,7 @@
|
||||
INSTALL(
|
||||
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DESTINATION ${DOC_DIR}
|
||||
PATTERN CMakeLists.txt EXCLUDE
|
||||
PATTERN Makefile.am EXCLUDE
|
||||
PATTERN doxygen/* EXCLUDE
|
||||
)
|
||||
88
project/jni/application/gemrb/gemrb/docs/en/CheatKeys.txt
Normal file
88
project/jni/application/gemrb/gemrb/docs/en/CheatKeys.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
CHEAT AND DEBUG KEYS
|
||||
--------------------
|
||||
|
||||
|
||||
Following is a list of cheats you can use during development of GemRB.
|
||||
If you implement a new cheatkey, try to assign the same key as in the
|
||||
original engine (if you implement a similar function).
|
||||
This list can be incomplete and obsolete - for current status, look into
|
||||
gemrb/plugins/Core/GameControl.cpp, functions GameControl::OnKeyRelease()
|
||||
and GameControl::Draw().
|
||||
|
||||
Cheat keys are disabled by default. To activate them, either type command
|
||||
GemRB.EnableCheatKeys(1) on console (pops-up with CTRL+SPACE key combination)
|
||||
or put it into some GUIScript - e.g. for PS:T in Start.py.
|
||||
|
||||
GameControl control must be focused for the keys to be recognized, so it
|
||||
might be needed to 'click' the GameControl (i.e. map) first.
|
||||
|
||||
|
||||
Ctrl-A - Alters the animation ID of the actor. You have to hover your
|
||||
mouse over it.
|
||||
|
||||
Ctrl-B - Draws path from start point marked by Ctrl-O to current mouse
|
||||
position.
|
||||
|
||||
Ctrl-C - Force casts a hardcoded spell. The last selected actor is the
|
||||
caster and the target is the door/actor currently under the
|
||||
pointer. This currently casts knock (SPWI207).
|
||||
|
||||
Ctrl-D - Trap or trapped container pointed w/ mouse is disarmed.
|
||||
|
||||
Ctrl-F - Toggles fullscreen mode
|
||||
|
||||
Ctrl-G - Dumps the global (game) object. Currently shows only loaded areas.
|
||||
|
||||
Ctrl-I - Triggers an interaction between the last pointed npc and a random
|
||||
party member.
|
||||
|
||||
Ctrl-J - Teleports (jumps) selected actors to current mouse position.
|
||||
|
||||
Ctrl-K - Kicks the actor out of the party.
|
||||
|
||||
Ctrl-L - Plays the S056ICBL animation over the actor. (This exists in PST only)
|
||||
TODO: iterate through animations, like the IE does.
|
||||
|
||||
Ctrl-M - Prints (on terminal or DOS window) useful info on pointed actor, door
|
||||
container or infopoint and current map
|
||||
|
||||
Ctrl-O - Marks current mouse position as start point (origin) for path drawn
|
||||
with Ctrl-B
|
||||
|
||||
Ctrl-P - Centers the viewport on the selected actor.
|
||||
|
||||
Ctrl-Q - The pointed actor will join the party.
|
||||
|
||||
Ctrl-R - Resurrects pointed actor. If she's already alive, just heals.
|
||||
|
||||
Ctrl-S - Alters the stance (animation state) of the actor. You have to
|
||||
hover your mouse over it.
|
||||
|
||||
Ctrl-T - Advances time by one hour.
|
||||
|
||||
Ctrl-V - Explores a small, random part of the pointed area.
|
||||
|
||||
Ctrl-X - Prints (on terminal or DOS window) name of current area script
|
||||
and current mouse position converted to game coordinates.
|
||||
|
||||
Ctrl-Y - Kills pointed actor or unlocks the pointed door/container, even
|
||||
if it requires a key.
|
||||
|
||||
Ctrl-Z - Same as Ctrl-A but backward
|
||||
|
||||
Ctrl-1 - Changes the armour level
|
||||
|
||||
Ctrl-4 - Toggles debug flag DEBUG_SHOW_INFOPOINTS (show all
|
||||
traps, infopoints and wallgroups)
|
||||
|
||||
Ctrl-6 - Toggles debug flag DEBUG_SHOW_LIGHTMAP (show the lightmap)
|
||||
|
||||
Ctrl-7 - Toggle drawing of Fog-Of-War (actually explored bitmap atm.)
|
||||
in GameControl.
|
||||
|
||||
Ctrl-8 - Toggle drawing of searchmap over the area in GameControl.
|
||||
|
||||
ALT - Toggles debug flag DEBUG_SHOW_CONTAINERS (show all containers
|
||||
and doors)
|
||||
|
||||
TAB - Sets debug flag DEBUG_XXX while pressed (unused)
|
||||
@@ -0,0 +1,9 @@
|
||||
Header file include order.
|
||||
|
||||
- Header file associated to cpp file, or plugin base class header.
|
||||
- Additional plugin headers.
|
||||
- Headers in includes.
|
||||
- Headers in core followed by subdirectories.
|
||||
- System and library headers.
|
||||
|
||||
Each item should be sorted in ascii order, and separated by blank lines.
|
||||
@@ -0,0 +1,36 @@
|
||||
There are 4 distinct palettes.
|
||||
|
||||
Known values of palette (p) are:
|
||||
0 Body
|
||||
1 Weapon
|
||||
2 Shield (or Off-hand weapon)
|
||||
3 Helmet
|
||||
|
||||
Each has 7 colour slots at most.
|
||||
Known values of slots (s) are:
|
||||
Body:
|
||||
0 - Metal
|
||||
1 - Minor
|
||||
2 - Major
|
||||
3 - Skin
|
||||
4 - Leather
|
||||
5 - Armor
|
||||
6 - Hair
|
||||
|
||||
Weapon:
|
||||
0 - Crossguard
|
||||
..
|
||||
5 - Grip
|
||||
6 - Blade
|
||||
|
||||
etc.
|
||||
|
||||
The combined palette location is given in <p><s> format, where both numbers are 4 bits.
|
||||
|
||||
|
||||
|
||||
In GemRB we store these values in the 7 stats starting at IE_COLORS.
|
||||
Each stat stores four colour gradients (each as a byte).
|
||||
The n-th stat contains the n-th colour for body, weapon, shield, helmet, from
|
||||
least significant to most significant byte.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
Container specification (based on IESDP)
|
||||
|
||||
Relevant structures:
|
||||
--------------------
|
||||
|
||||
typedef struct {
|
||||
char containername[32];
|
||||
short posx, posy;
|
||||
short type;
|
||||
short lockdiff;
|
||||
long flags;
|
||||
short trapdetect;
|
||||
short trapremove;
|
||||
short trapped;
|
||||
short trapvisible;
|
||||
short launchx, launchy;
|
||||
short p1x, p1y, p2x, p2y; //minimum bounding box
|
||||
long firstitem;
|
||||
long itemcount;
|
||||
char trapscript[8];
|
||||
long firstvertex;
|
||||
short vertexcount;
|
||||
short unknown56;
|
||||
char scriptname[32];
|
||||
char keyitem[8];
|
||||
short unknown80;
|
||||
short unknown82;
|
||||
long strref; //STRREF!!!
|
||||
char unused[56];
|
||||
} area_container;
|
||||
|
||||
|
||||
Description:
|
||||
------------
|
||||
The container's name is not really relevant, it is used only in editors. Scripts reference a container by
|
||||
the scriptname field. The container's position determines the opening location.
|
||||
All container types are similar, except that only a few got a special graphics (BAM), see containr.2da in GemRB.
|
||||
The ground piles are special containers of type 4. Ground pile containers show the item's ground icon instead of
|
||||
the container's trigger polygon. Containers could be locked, the lockdiff field determines the difficulty to open
|
||||
them by force. The trapdetect and trapremove fields determine the trap detection and removal difficulties.
|
||||
The trapped field shows if the trap is still trapped, and the trapvisible field (GemRB implements it as a countdown)
|
||||
shows if the trap is still visible. The launch point determines the origin of spells cast by the trap script.
|
||||
The bounding box has effect on the polygon only. The trapscript is not necessarily a trap, this script is always running
|
||||
not like the door's script. There are several triggers that might affect this script. (OpenFail, Open, Lockpick fail).
|
||||
The firstitem/itemcount fields point to the inventory of the container (or ground pile).
|
||||
The keyitem determines if there is a key. (There should be a keyremoval field like with doors, but it isn't yet found).
|
||||
The strref is the openfail message (when it is 100% impossible to open).
|
||||
|
||||
Container flags:
|
||||
----------------
|
||||
0x00000001 - The container is locked, opening by brute force, key or lockpick is needed.
|
||||
0x00000002 - ?
|
||||
0x00000004 - ?
|
||||
0x00000008 - The script isn't removed when it is triggered.
|
||||
0x00000010 - ?
|
||||
0x00000020 - The container is disabled, the polygon is still active, but the container is not accessible to PC's
|
||||
101
project/jni/application/gemrb/gemrb/docs/en/Engine/Doors.txt
Normal file
101
project/jni/application/gemrb/gemrb/docs/en/Engine/Doors.txt
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
Door specification (based on IESDP)
|
||||
|
||||
Relevant structures:
|
||||
--------------------
|
||||
|
||||
typedef struct {
|
||||
char doorid[8]; //matched with area_door.doorid
|
||||
short closed; //this is largely unknown, probably it shows which tile represents the closed state
|
||||
short firstdoortileidx; //door tile cell indices are 16 bit values
|
||||
short countdoortileidx;
|
||||
short countpolygonopen; //wallpolygon count for the open door state
|
||||
short countpolygonclose; //wallpolygon count for the closed door state
|
||||
long offsetpolygonopen; //wallpolygon offset for the open door state
|
||||
long offsetpolygonclose; //wallpolygon offset for the closed door state
|
||||
} wed_door;
|
||||
|
||||
typedef struct {
|
||||
char doorname[32]; //scripting name
|
||||
char doorid[8]; //wed reference
|
||||
long flags; //door flags, see below
|
||||
long firstvertexopen; //first open vertex index
|
||||
short countvertexopen; //count of open vertices
|
||||
short countvertexclose; //count of closed vertices
|
||||
long firstvertexclose; //first closed vertex index
|
||||
short op1x,op1y,op2x,op2y; //minimum bounding box for open
|
||||
short cp1x,cp1y,cp2x,cp2y; //minimum bounding box for close
|
||||
long firstblockopen; //first open impeded blocks (for searchmap)
|
||||
short countblockopen; //count of open impeded blocks
|
||||
short countblockclose; //count of closed impeded blocks
|
||||
long firstblockclose; //first closed impeded blocks (for searchmap)
|
||||
long unknown54; //unused in tob
|
||||
char openres[8]; //opening sound resource reference
|
||||
char closeres[8]; //closing sound resource reference
|
||||
long cursortype; //mouse cursor type (when mouse is over the door polygon)
|
||||
short trapdetect; //difficulty of detecting a trap
|
||||
short trapremoval; //difficulty of removing a trap
|
||||
short trapflags; //
|
||||
short trapdetflags; //
|
||||
short launchx, launchy; //traps are launched from this point
|
||||
char key[8]; //key item resource reference
|
||||
char openscript[8]; //(trap) script, activated at first triggering (opening door)
|
||||
long locked; //
|
||||
long lockremoval; //difficulty of opening the door
|
||||
short locp1x, locp1y; //open location 1
|
||||
short locp2x, locp2y; //open location 2
|
||||
long strref; //check this for missing strings STRREF!!!
|
||||
char regionlink[24];
|
||||
long nameref; //check this for missing strings STRREF!!!
|
||||
char dlgref[8]; //?
|
||||
char unknownc0[8]; //?
|
||||
} area_door;
|
||||
|
||||
Description:
|
||||
------------
|
||||
|
||||
Scripts reference the door using the doorname field (Scripting Name).
|
||||
The relation between the two structures (wed/area) is established by the doorid field.
|
||||
Each door is a 'wall', the wall polygons are stored in the wed part of the door.
|
||||
The doors also reference two sets of tiles (also in the wed).
|
||||
Each door is a special info point with a separate trigger area (polygon) for the open/closed state.
|
||||
The bounding boxes are used when rendering the trigger area polygons (actually, could be auto-generated).
|
||||
The impeded blocks are superpositioned on the searchmap, they block passage, vision (unless door is transparent) or
|
||||
changing the door's state (unless door is sliding).
|
||||
The opening/closing sounds of a door are modifiable, though there is a default one (PST has no default sounds).
|
||||
If a door is hidden, then the default sound is different.
|
||||
The cursor type applies when the mouse is over the trigger area of the door (both states have the same cursor).
|
||||
If a door has a script assigned and the trap is detectable the trap detection difficulty determines the chance of
|
||||
success. If the difficulty is exactly 100, then it is impossible to detect (a flaw in the original engine lets >100
|
||||
values to be detectable). In the impossible case, a message (strref) will be displayed.
|
||||
The trap removal difficulty is similarly handled. If trapflags is nonzero, then the trap is still active.
|
||||
If Trapdetflags is nonzero, then the trap is still visible. (GemRB will implement the latter as a counter which
|
||||
counts down until 0). The trap (if the script starts a spell) is fired from the launch point.
|
||||
The key resource has meaning only if the door is locked. If it is empty, then the door cannot be unlocked by key.
|
||||
The door is also openable by brute force, the difficulty for this is stored in lockremoval.
|
||||
The actor who operates the door will walk up to the closest of the open locations.
|
||||
Regionlink is a reference to a travel region which is blocked by the door, if the door is closed.
|
||||
//not tested
|
||||
Nameref is the name of the door in case of a dialog. DlgRef is the door's dialog.
|
||||
|
||||
The door flags are these:
|
||||
|
||||
Value Meaning if set
|
||||
----- --------------
|
||||
0x00000001 - The door is closed (PST), the door is open (other games). When a door's state is changed,
|
||||
(OpenDoor/CloseDoor actions), its (trap) script is triggered.
|
||||
0x00000002 - The door is locked, the lock difficulty must be set to nonzero to have this any effect.
|
||||
0x00000004 - The script isn't removed when it is triggered.
|
||||
0x00000008 - The trap (script) is detectable (fair to set it for real traps).
|
||||
0x00000010 - Broken?
|
||||
0x00000020 - Can't close?
|
||||
0x00000040 - An info trigger is linked to this door.
|
||||
0x00000080 - Secret door
|
||||
0x00000100 - Secret door already found (purple outline)
|
||||
0x00000200 - The impeded doors are ignored concerning vision (door is transparent)
|
||||
0x00000400 - The key object is removed when unlocking the door
|
||||
0x00000800 - The impeded blocks are ignored when opening the door (sliding door)
|
||||
0x00001000 - ?
|
||||
0x00002000 - ?
|
||||
0x00004000 - ?
|
||||
0x00008000 - ?
|
||||
116
project/jni/application/gemrb/gemrb/docs/en/Engine/Effects.txt
Normal file
116
project/jni/application/gemrb/gemrb/docs/en/Engine/Effects.txt
Normal file
@@ -0,0 +1,116 @@
|
||||
A kind of 'specification' for the IE game effects.
|
||||
This description uses the IESDP effect structure definition with updates.
|
||||
|
||||
V1.0 effect struct (you need to convert it to V2 on the fly)
|
||||
typedef struct {
|
||||
short feature; //opcode
|
||||
unsigned char target; //target type
|
||||
unsigned char power; //level
|
||||
parameter par1;
|
||||
parameter par2;
|
||||
unsigned char timing; //timing method
|
||||
unsigned char resist; //resistance type
|
||||
long duration;
|
||||
unsigned char prob2; //usually 100
|
||||
unsigned char prob1; //usually 0
|
||||
char resource[8]; //1. resource
|
||||
long count;
|
||||
long sides;
|
||||
long stype;
|
||||
long sbonus;
|
||||
long unknown2c; //unused in V1.0, but copied over to 2.0
|
||||
} feat_block;
|
||||
|
||||
//effect body (V2.0 effects)
|
||||
//please note that in an .eff file there is an additional header
|
||||
//before these (the first 8 bytes are doubled)
|
||||
Offset Size (data type) Description
|
||||
0x0000 4 (char array) For on disk effects, this is a copy of the Signature field from the header. For embedded EFF V2.0 structures, this is zeroed out.
|
||||
0x0004 4 (char array) For on disk effects, this is a copy of the Version field from the header. For embedded EFF V2.0 structures, this is zeroed out.
|
||||
0x0008 4 (dword) Effect type
|
||||
0x000c 4 (dword) Target type
|
||||
0x0010 4 (dword) Power (level)
|
||||
0x0014 4 (dword) NP1
|
||||
0x0018 4 (dword) NP2
|
||||
0x001c 4 (dword) Flags (timing method)
|
||||
0 - duration
|
||||
1 - permanent
|
||||
2 - while equipped (source of effect)
|
||||
3 - delayed duration (after delay duration)
|
||||
4 - delayed (after delay it is permanent)
|
||||
5 - special, delayed, unsaved
|
||||
6 - special, duration
|
||||
7 - special, ?
|
||||
8 - permanent, unsaved
|
||||
9 - permanent after death
|
||||
10 - trigger (just expired)
|
||||
|
||||
0x0020 4 (dword) Time (duration)
|
||||
0x0024 2 (word) Probability 1
|
||||
0x0026 2 (word) Probability 2
|
||||
0x0028 8 (resref) resource
|
||||
0x0030 4 (dword) die sides/max level
|
||||
0x0034 4 (dword) dice count/min level
|
||||
0x0038 4 (dword) save type (stype)
|
||||
0x003c 4 (dword) save bonus (sbonus)
|
||||
0x0040 4 (dword) Is Variable? (same as 0x2c in EFF V1.0)
|
||||
0x0044 4 (dword) Spell School (used for dispelling)
|
||||
0x0048 4*3 (dword) Unknown
|
||||
0x0054 4 (dword) Resistance Type (resist)
|
||||
0x0058 4 (dword) NP3
|
||||
0x005c 4 (dword) NP4
|
||||
0x0060 4*2 (dword) unknown
|
||||
0x0068 8 (resref) VVC
|
||||
0x0070 8 (resref) 3. resource
|
||||
0x0078 4*2 (point) Source point
|
||||
0x0080 4*2 (point) Target point
|
||||
0x0088 4 (dword) Source type of effect (0 - none, 1 - item, 2 - spell)
|
||||
0x008c 8 (resref) Source of Effect (used for equipping and dispelling)
|
||||
0x0094 4 (dword) resource flags
|
||||
0x0098 4 (dword) projectile
|
||||
0x009c 4 (dword) item slot
|
||||
0x00a0 32 (bytes) variable
|
||||
0x00c0 4*2 (dword) unknown
|
||||
0x00c8 4 (dword) Secondary Type (used for dispelling)
|
||||
0x00cc 4*15 (dword) unknown
|
||||
|
||||
|
||||
An effect's lifecycle:
|
||||
(apply time)
|
||||
1. Check if it affects the target
|
||||
- if percentages don't match, drop it
|
||||
- if level limits don't match, drop it (some effects don't have this)
|
||||
- if resistable, check for resistance (once for an applied block)
|
||||
- if saving throw applies, check for it (once for an applied block)
|
||||
2. If it is a delayed effect
|
||||
- precalculate the time of onset (in game time) (store it in the duration field).
|
||||
- put it on the fx list
|
||||
3. If it is an instant effect (some opcodes ignore delays, so they are always instant)
|
||||
|
||||
(in each update cycle in creatures)
|
||||
1. copy the original stats to the modified stats
|
||||
2. apply all effects in their original order
|
||||
- if the effect isn't in time (delayed or delayed duration, 3 or 4), skip it
|
||||
- if a delayed effect reached time: change it to permanent (i think this is a different permanent)
|
||||
- if a delayed duration effect reached time: change it to duration
|
||||
- if the effect is permanent (1 or 9), apply it (some effects couldn't be permanent, these effects just apply, then go away)
|
||||
- if the effect reached end (duration), set a special timing method (10)
|
||||
- if an effect reached expiration (10) remove it from the queue
|
||||
|
||||
(dispelling/removal)
|
||||
There should be a way of:
|
||||
1. Dispelling all by power level ( remove all effects <= power level)
|
||||
2. Dispelling all by source of effect ( remove all effects == soe)
|
||||
3. Dispelling all or first that matches a spell school or secondary type
|
||||
- there is a 'dispellable' flag in the effects which normally disables dispelling. But there should be a 'forced dispel' flag.
|
||||
4 On death: remove all effects except (9)
|
||||
|
||||
(saving)
|
||||
Effects that are not saved: 2 -'while equipped'
|
||||
|
||||
|
||||
(probability, gemrb specific)
|
||||
If the low probability field was set to 100, then the high probability field contains the
|
||||
stat which determines the chance of the effect. For example:
|
||||
100/136 would determine the chance of the effect based on the caster's detect illusions skill.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
enginedocs_DATA = *.txt
|
||||
enginedocsdir = $(docdir)/Engine/
|
||||
EXTRA_DIST = *.txt
|
||||
@@ -0,0 +1,38 @@
|
||||
Projectile extension flags for GemRB
|
||||
|
||||
These flags reside in the .pro format on offset 0x2c
|
||||
|
||||
1 Bounce from walls
|
||||
2 Continue on original path as travel projectile after explosion
|
||||
4 Freeze as travel projectile after explosion
|
||||
8 No travel path (move to destination immediately, thus skipping the travel phase)
|
||||
16 Trail bams got the same orientation behaviour as the travel bam
|
||||
32 Curved path
|
||||
64 Random starting frame for travel projectile
|
||||
128 Pillar type projectiles (instead of orientations, the cycles are drawn vertically on top of each other)
|
||||
256 Half transparent travel projectile (not blend, but could be combined with it)
|
||||
512 Static tinted travel projectile
|
||||
1024 Create another projectile with an ID one less than the current one (missile iteration)
|
||||
2048 Tile the whole area of effect with the travel bam
|
||||
4096 Freefalling trajectory (appears horizontally over the target)
|
||||
8192 Incoming trajectory (appears diagonally over the target)
|
||||
16384 The area of effect is a line from source to target
|
||||
32768 The area of effect is a line (wall) crossing target, ahead of source
|
||||
65536 Draw behind (under) the target
|
||||
0x20000 Draw pop in/hold/pop out animation sequence
|
||||
0x40000 Internal flag for 0x20000 (after pop in phase was done) - could be used to play 2 phases, first shadow, then travel bam
|
||||
0x80000 Slowly fade out a freezed projectile (used with flag 0x4)
|
||||
0x100000 Display string in setup (string reference is stored on offset 0x30)
|
||||
0x200000 Random movement instead of path
|
||||
|
||||
These fields are in the .pro format in the Extension structure:
|
||||
0x228 - ResRef - spread animation (RESOURCE2 in areapro.2da)
|
||||
0x230 - ResRef - secondary animation (RESOURCE3 in areapro.2da)
|
||||
0x238 - ResRef - area sound (SOUND2 in areapro.2da)
|
||||
0x240 - Dword - flags in areapro.2da
|
||||
|
||||
Other areapro.2da fields
|
||||
0x21c - ResRef - RESOURCE1
|
||||
0x208 - ResRef - SOUND1
|
||||
|
||||
See areapro.2da for more information.
|
||||
@@ -0,0 +1,8 @@
|
||||
Door Container Travel Trap Info Actor
|
||||
LastEntered Opened - - Entered/IsOverMe - LastHitter
|
||||
LastTrigger Closed - - - Clicked LastSummoner
|
||||
LastUnlocked Unlocked - - - - LastTalkedTo
|
||||
LastDisarmFailed DisarmFailed DisarmFailed - DisarmFailed - LastTarget
|
||||
LastDisarmed Disarmed Disarmed - Disarmed - LastAttacker
|
||||
LastOpenFailed OpenFailed OpenFailed -
|
||||
PickPocketFailed
|
||||
@@ -0,0 +1,5 @@
|
||||
Usability hacks in the original IE engine
|
||||
|
||||
(0x00040000) Assasin - 15 skill points (skills.2da)
|
||||
(0x00080000) Bountyhunter - 20 skill points (skills.2da)
|
||||
(0x00100000) Swashbuckler - no backstab multiplier (TODO)
|
||||
@@ -0,0 +1,29 @@
|
||||
Changes to the original engines
|
||||
*******************************
|
||||
|
||||
|
||||
Generally the changes were designed to be compatible with the original game files. No additional fields added (so far), only previously unused parts are used.
|
||||
|
||||
1. Items entries:
|
||||
Item entries in stores, containers, creatures will have additional flags. These flags will extend the usage of HasItem, DestroyItem, TakeItem scripting functions. The first four flags should be familiar.
|
||||
|
||||
IE_ITEM_IDENTIFIED = 1
|
||||
IE_ITEM_UNSTEALABLE = 2
|
||||
IE_ITEM_STOLEN = 4
|
||||
IE_ITEM_UNDROPPABLE = 8
|
||||
//these are GemRB extensions
|
||||
IE_ITEM_ACQUIRED = 0x10
|
||||
IE_ITEM_DESTRUCTIBLE = 0x20
|
||||
IE_ITEM_EQUIPPED = 0x40
|
||||
IE_ITEM_STACKED = 0x80
|
||||
|
||||
This will make possible scripts like these:
|
||||
HasAnyItem(STOLEN)
|
||||
TakeAllStolenItem()
|
||||
DropAllEquippedItem()
|
||||
HasItem("SWORD",EQUIPPED|IDENTIFIED)
|
||||
|
||||
2. Journal entries:
|
||||
Journal entries will have an additional Group ID. This group ID will make it possible to remove a group of journal entries along with adding a new one of the same group using solely the dialog structure. This eliminates the problem of residue entries. Also, you don't have to remember the journal entry strref, just assign a unique group (quest ID).
|
||||
The quest ID byte will be stored on offset 0x0002 in the transition table entry.
|
||||
The former flags field (it was a dword) will be a word. Its highest bit will be the group flag. Unless this flag is set, the behaviour will be the same as before. If you set this flag, then all journal entries set with the same id will be removed before adding the new one. The scripting actions will also accept an additional quest ID parameter.
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
Prototype: ActOnPC(player)
|
||||
|
||||
Metaclass Prototype: /
|
||||
|
||||
Description: Targets the selected PC for an action (cast spell, attack, ...)
|
||||
|
||||
Parameters: player - the pc's party position (1-10)
|
||||
|
||||
Return value: /
|
||||
|
||||
See also: ClearActions, SetModalState, SpellCast
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Prototype: GemRB.AddGameTypeHint(type, weight, flags=0)
|
||||
|
||||
Description: Asserts that GameType should be TYPE, with confidence WEIGHT.
|
||||
This is used by Autodetect.py scripts when GameType was
|
||||
set to 'auto' in config file.
|
||||
|
||||
Parameters: type - GameType (e.g. bg1, bg2, iwd, how, iwd2, pst and possibly others)
|
||||
weight - numeric, confidence that TYPE is correct. Standard games should use
|
||||
values <= 100, (eventual) new games based on the standard ones
|
||||
should use values above 100.
|
||||
flags - numeric, not used now
|
||||
|
||||
|
||||
Return value: N/A
|
||||
|
||||
MD5: 1c130785e329448b743a7f160b07a312
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
Prototype: AdjustScrolling(WindowIndex, ControlIndex, x, y)
|
||||
|
||||
Metaclass Prototype: AdjustScrolling(x, y)
|
||||
|
||||
Description: Sets the scrolling offset of a WorldMapControl.
|
||||
|
||||
Parameters: WindowIndex, ControlIndex - the control's reference
|
||||
x,y - scrolling offset values
|
||||
|
||||
Return value: N/A
|
||||
|
||||
Example:
|
||||
#northeast
|
||||
Button = GemRB.GetControl (Window, 9)
|
||||
GemRB.SetEvent (Window, Button, IE_GUI_BUTTON_ON_PRESS, "MapNE")
|
||||
...
|
||||
def MapNE():
|
||||
GemRB.AdjustScrolling (Window, WorldMapControl, 10, -10)
|
||||
return
|
||||
The above lines set up a button event. When the button is pressed the worldmap will be shifted in the northeastern direction.
|
||||
|
||||
See also: CreateWorldMapControl
|
||||
|
||||
|
||||
MD5: 500775c70886362ce1ebc9353505027f
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
Prototype: GemRB.ApplyEffect(PartyID, opcode, param1, param2[, resref, resref2, resref3, source])
|
||||
|
||||
Description: Creates a basic effect and applies it on the player character
|
||||
marked by PartyID.
|
||||
This function could be used to add stats that are stored in effect blocks.
|
||||
|
||||
Parameters:
|
||||
PartyID - the player character's index in the party
|
||||
opcode - the effect opcode (for values see effects.ids)
|
||||
param1 - parameter 1 for the opcode
|
||||
param2 - parameter 2 for the opcode
|
||||
resref - optional resource reference to set in effect
|
||||
resref2 - (optional) resource reference to set in the effect
|
||||
resref3 - (optional) resource reference to set in the effect
|
||||
resref4 - (optional) resource reference to set in the effect
|
||||
source - (optional) source to set in the effect
|
||||
|
||||
Return value: N/A
|
||||
|
||||
Example:
|
||||
for i in range(ProfCount-8):
|
||||
StatID = GemRB.GetTableValue(TmpTable, i+8, 0)
|
||||
Value = GemRB.GetVar ("Prof "+str(i) )
|
||||
if Value:
|
||||
GemRB.ApplyEffect (MyChar, "Proficiency", Value, StatID )
|
||||
|
||||
The above example sets the weapon proficiencies in a bg2's CharGen9.py script.
|
||||
|
||||
See also: SpellCast, SetPlayerStat, GetPlayerStat, CountEffects
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
Prototype: GemRB.ApplySpell(PartyID, resref)
|
||||
|
||||
Description: Applies a spell on the actor marked by PartyID.
|
||||
This function can be used to add abilities that are stored as spells (eg. innates)
|
||||
|
||||
Parameters:
|
||||
PartyID - the player character's index in the party
|
||||
resref - spell resource reference
|
||||
|
||||
Return value: N/A
|
||||
|
||||
See also: SpellCast, ApplyEffect, CountEffects
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
Prototype: AttachScrollBar(WindowIndex, ControlIndex, ScrollBarControlIndex)
|
||||
|
||||
Description: Attaches a scrollbar to a control. If the control receives mousewheel events, it will be relayed to the ScrollBar. TextArea controls will also be synchronised with the scrollbar. If there is a single ScrollBar on the window, or the ScrollBar was set with SetDefaultScrollBar, this command is not needed.
|
||||
|
||||
Parameters: WindowIndex, ControlIndex - the control's reference
|
||||
ScrollBarControlIndex - the scrollbar's index on the same window
|
||||
|
||||
Return value: N/A
|
||||
|
||||
See also: ConvertEdit, SetDefaultScrollBar
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
Prototype: GemRB.CanUseItemType(slottype, itemname[, actor, equipped])
|
||||
|
||||
Description: Checks the itemtype vs. slottype, and also checks the usability flags vs. Actor's stats (alignment, class, race, kit etc.)
|
||||
|
||||
Parameters:
|
||||
slottype - the slot to check (See ie_slots.py)
|
||||
itemname - the resource reference of the item
|
||||
actor - the actor's number in the team (if 0, then actor is not unimportant)
|
||||
equipped - whether the item is equipped (if so, don't consider disabled items to be unusable)
|
||||
|
||||
Return value: boolean
|
||||
|
||||
See also: DropDraggedItem, UseItem
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user