diff --git a/.devsPrefs/AquariusPower/.gitignore.AquariusPower b/.devsPrefs/AquariusPower/.gitignore.AquariusPower
index 04bc83fd9..de0e92646 100644
--- a/.devsPrefs/AquariusPower/.gitignore.AquariusPower
+++ b/.devsPrefs/AquariusPower/.gitignore.AquariusPower
@@ -82,3 +82,6 @@ dbgmsg.cpp
/SDL2-2.0.4/
build
build.*
+.kdev_include_paths
+/.devsPrefs/AquariusPower/nbproject/private/*
+/nbproject
diff --git a/.devsPrefs/AquariusPower/TODO.txt b/.devsPrefs/AquariusPower/TODO.txt
new file mode 100644
index 000000000..ce3bcdc27
--- /dev/null
+++ b/.devsPrefs/AquariusPower/TODO.txt
@@ -0,0 +1,46 @@
+FIX-CRITICAL
+
+FIX-IMPORTANT
+- validate the existance of all generated savegame files, it is stored in the .sav file, would crash if missing so better just promptly look for them...
+- disappearing pet angel wont drop all equipped items (the ones we give'm and would not normally disappear...), namely torso armor at least
+
+FIX-MINOR
+- by remembering the previously selected entry list, sometimes the wrong savegame is being loaded, not the "apparently" selected one...
+- skeleton dog shouldn't pee..., but could say "tries to pee but nothing happens" :)
+- pet with poly ring and polycontrol ring, polymorphing into ghost then into bunny, when back to human will not re-equip
+- deny showitemsunder if lantern is on the wall? (amulet of phasing)
+- when felist is open, still showing tiny dungeon animated behind...
+- the last entry in the savegame list, even after imported, may still show as being in the previous version and after importing a new player name may be used and so a new savegame created, quite a mess... (quite confuse this one, may be a unique bug that only happens when importing some specific random rare old saves...)
+
+IMPROVEMENTS
+- mine::WillExplode() if stepper is trying to pickup, check if levitating and 25+ dexterity (or only dexterity but harder like x2) to let it be disarmed and safely picked up
+- craft dismantle w/o forge should require dagger and hammer if has any meltable
+ - craft test material should allow broken things too
+ - craft if sat on a chair should lower craft time too
+ - craft tools should take damage too, mainly if the worked materials are harder
+- felist: default remember_selected
+- simple pile equal items on "show items under", ex.: if more than 1, simply draw a tiny + sign with 2 drawn white lines
+ - show items under, begin with the last one, unless if showing them all in lines
+- mini map unfold slowly once per frame one dungeon line of squares animation
+- remove flag CanBeDestroyed from items that can be crafted? or deny their crafting? or keep as on fumble creates lump?
+- show material Str only if in possession of the materials book
+ - inventory 'i' will be selectable if in possession of the materials book and chosing will show at most 2 lines as filter to the book
+- pickup accept many using the same pickup key ',' at least see the other keys cfgs too
+
+NEW STUFF
+- draw the current equipped amulet on the player
+- sfx "It smells oddly intelligent here." the good smoke as opposed to "a sinister stench surrounds you" the bad smoke
+
+==================================================================================================================
+DONE:
+- add a lsquare token during detect material to highlight the square, the guessed luminance fails at daylight 13:02h
+- quick swap weapons hotkey, to also reequip after mining interrupted
+- hum?? walk over friendly: chat and wait one turn to let it move
+- ?make pet always follow very closely to avoid getting lost from the player? or that is part of the troubles/difficulty? :)
+- make pet look at last 3 squares player walked before waiting
+- sfx for .*hit.* it is matching white :P
+- list item zoom xBRZ, draw the background behind it to make the blending work properly all times!
+- craft fluid extraction should not require a specific previous location to resume
+- dupPlayerBugFix: check also for duplicity of TrapIDMap as crashing on save
+- optionally auto inscribe some map notes like altars, anvil, chair etc
+- the materials book could be filtered by typing (felist already can filter anything)
diff --git a/.devsPrefs/AquariusPower/nbproject/configurations.xml b/.devsPrefs/AquariusPower/nbproject/configurations.xml
new file mode 100644
index 000000000..47597a70f
--- /dev/null
+++ b/.devsPrefs/AquariusPower/nbproject/configurations.xml
@@ -0,0 +1,332 @@
+
+
+
+
+
+
+
+
+
+
+ felist.h
+ feloops.h
+ femath.h
+ festring.h
+ fetime.h
+ graphics.h
+ hscore.h
+
+
+ bitmap.cpp
+ config.cpp
+ dbgmsg.cpp
+ error.cpp
+ febot.cpp
+ feio.cpp
+ felist.cpp
+ femath.cpp
+ festring.cpp
+ fetime.cpp
+ graphics.cpp
+ hscore.cpp
+ rawbit.cpp
+ save.cpp
+ specialkeys.cpp
+ whandler.cpp
+
+
+ action.cpp
+ actions.cpp
+ actset.cpp
+ area.cpp
+ areaset.cpp
+ bodypart.cpp
+ bugworkaround.cpp
+ char.cpp
+ charset.cpp
+ charsset.cpp
+ cmdcraft.cpp
+ cmdswapweap.cpp
+ command.cpp
+ cont.cpp
+ coreset.cpp
+ database.cpp
+ dataset.cpp
+ definesvalidator.cpp
+ devcons.cpp
+ dungeon.cpp
+ entity.cpp
+ fluid.cpp
+ game.cpp
+ gear.cpp
+ god.cpp
+ gods.cpp
+ godset.cpp
+ hiteffect.cpp
+ iconf.cpp
+ id.cpp
+ igraph.cpp
+ item.cpp
+ itemset.cpp
+ level.cpp
+ levelset.cpp
+ main.cpp
+ materia.cpp
+ materias.cpp
+ materset.cpp
+ message.cpp
+ object.cpp
+ room.cpp
+ rooms.cpp
+ roomset.cpp
+ script.cpp
+ slot.cpp
+ slotset.cpp
+ stack.cpp
+ trap.cpp
+ traps.cpp
+ trapset.cpp
+ wmapset.cpp
+ worldmap.cpp
+ wskill.cpp
+ wsquare.cpp
+ wterra.cpp
+ wterras.cpp
+
+
+
+
+ CMakeLists.txt
+ Makefile
+ nbproject/private/launcher.properties
+
+
+ ^(nbproject)$
+
+ audio
+ fantasyname
+ FeLib/Source
+ xbrzscale
+ Main/Source
+ Main/Include
+ FeLib/Include
+
+ Makefile
+
+
+
+ default
+ false
+ false
+
+
+
+
+
+ .
+ ${MAKE} -f Makefile
+ ${MAKE} -f Makefile clean
+
+
+
+ .
+ ${CMAKE} -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${IDE_CC} -DCMAKE_CXX_COMPILER=${IDE_CXX} -DCMAKE_C_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_CXX_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
+
+
+
+
+
+ default
+ false
+ false
+
+
+
+
+
+ .
+ ${MAKE} -f Makefile
+ ${MAKE} -f Makefile clean
+
+
+
+ DBGMSG
+ FELIST_WAITKEYUP
+ FIX_LARGECREATURE_TELEPORT_GLITCH
+ UNIX
+ USE_SDL
+ WIZARD
+
+
+
+
+ .
+ ${CMAKE} -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${IDE_CC} -DCMAKE_CXX_COMPILER=${IDE_CXX} -DCMAKE_C_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_CXX_FLAGS_DEBUG="-g3 -gdwarf-2" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
+
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+ -
+
+
+
+
diff --git a/.devsPrefs/AquariusPower/nbproject/project.xml b/.devsPrefs/AquariusPower/nbproject/project.xml
new file mode 100644
index 000000000..491b5d418
--- /dev/null
+++ b/.devsPrefs/AquariusPower/nbproject/project.xml
@@ -0,0 +1,36 @@
+
+
+ org.netbeans.modules.cnd.makeproject
+
+
+ Ivan.github
+
+ cc,cpp
+ h
+ UTF-8
+
+
+ audio
+ fantasyname
+ FeLib/Source
+ xbrzscale
+ Main/Source
+ Main/Include
+ FeLib/Include
+
+
+
+ Default
+ 0
+
+
+ myCfg
+ 0
+
+
+
+ false
+
+
+
+
diff --git a/Doc/Misc/DefinesValidator.txt b/Doc/Misc/DefinesValidator.txt
index 0aa1731e3..0b2e9b97e 100644
--- a/Doc/Misc/DefinesValidator.txt
+++ b/Doc/Misc/DefinesValidator.txt
@@ -6,7 +6,7 @@ or when someone changes a value only at `define.dat` or at some c++ .h file, so
2) compile and install the game.
-3) go to that option and toggle it to yes.
+3) run any game, go to the console ctrl+` and run the generator
4) the file `definesvalidator.h` will be created at the same path where the `ivan.conf` is located.
@@ -15,4 +15,4 @@ or when someone changes a value only at `define.dat` or at some c++ .h file, so
6) compile and install again so that the validator will know what value the c++ `#define` should have, based on the define.dat file.
in case c++ has no such define, it will be ignored ex.: the check will be performed only `#ifdef EMISSARY` (currently not used in c++ .h files)
-7) run and activate that option to `yes`, it will abort complaining about TORSO value with this message: "Defined TORSO with value 2 from .dat file mismatches hardcoded c++ define value of 1!"
+7) run any game, go to the console ctrl+` and run the validator, it will abort complaining about TORSO value with this message: "Defined TORSO with value 2 from .dat file mismatches hardcoded c++ define value of 1!"
diff --git a/FeLib/Include/feio.h b/FeLib/Include/feio.h
index 0b8a7b2d9..59e6e3d64 100644
--- a/FeLib/Include/feio.h
+++ b/FeLib/Include/feio.h
@@ -17,6 +17,7 @@
#include "festring.h"
class bitmap;
+class inputfile;
typedef truth (*stringkeyhandler)(int, festring&);
typedef void (*bitmapeditor)(bitmap*, truth);
@@ -44,6 +45,7 @@ class iosystem
col16 = 0xFFFF, truth = true,
truth = true, bitmapeditor = 0);
static truth IsOnMenu();
+ static bool IsInUse();
};
#endif
diff --git a/FeLib/Include/felist.h b/FeLib/Include/felist.h
index ea9182b25..857ef03d6 100644
--- a/FeLib/Include/felist.h
+++ b/FeLib/Include/felist.h
@@ -40,9 +40,11 @@ class felist
~felist();
void AddEntry(cfestring&, col16, uint = 0,
uint = NO_IMAGE, truth = true);
+ void SetLastEntryHelp(cfestring Help);
void AddDescription(cfestring&, col16 = WHITE);
static void SetAllowMouse(bool b);
uint Draw();
+ uint ScrollToLastPage(bool& JustSelectMoveOnce,bitmap& BackGround,bitmap* Buffer);
void SetFirstDrawNoFade(bool b);
uint GetMouseSelectedEntry(v2 v2MousePos);
void QuickDraw(bitmap*, uint) const;
@@ -78,14 +80,24 @@ class felist
void SetEntryDrawer(entrydrawer What) { EntryDrawer = What; }
static truth isAnyFelistCurrentlyDrawn();
static bool PrepareListItemAltPosBackground(blitdata& rB,bool bAltPosFullBkg);
+ static void SetListItemAltPosMinY(int iY);
static v2 GetCurrentListSelectedItemPos(){return v2SelectedPos;};
static void SetSelectedBkgColor(col16 col){colSelectedBkg=col;}
void SetOriginalPos(v2 pos){v2OriginalPos = pos;};
+ void ClearFilter();
private:
+ void PrepareToReturn();
+ void ApplyFilter();
+ void UpdateFilterDesc();
+ void SetFilter(festring Filter);
+ festring GetFilter();
+ uint DrawFiltered(bool& bJustExitTheList);
void DrawDescription(bitmap*) const;
bool FirstDrawNoFade;
std::vector vEntryRect;
std::vector Entry;
+ std::vector EntryBkp;
+ bool bJustRestoreEntries;
std::vector Description;
uint PageBegin;
uint Maximum;
diff --git a/FeLib/Include/graphics.h b/FeLib/Include/graphics.h
index a547b165e..68ca890c8 100644
--- a/FeLib/Include/graphics.h
+++ b/FeLib/Include/graphics.h
@@ -55,7 +55,7 @@ class graphics
static void DrawRectangleOutlineAround(bitmap* bmpAt, v2 v2TopLeft, v2 v2Border, col16 color, bool wide);
static void BlitDBToScreen();
- static void DrawAboveAll(bitmap* bmpDest);
+ static void DrawAboveAll(bitmap* bmpBuffer);
static void AddDrawAboveAll(drawabove da, int iPriority, const char* desc);
static v2 GetRes() { return Res; }
diff --git a/FeLib/Include/specialkeys.h b/FeLib/Include/specialkeys.h
new file mode 100644
index 000000000..e467e7d8f
--- /dev/null
+++ b/FeLib/Include/specialkeys.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Iter Vehemens ad Necem (IVAN)
+ * Copyright (C) Timo Kiviluoto
+ * Released under the GNU General
+ * Public License
+ *
+ * See LICENSING which should be included
+ * along with this file for more details
+ *
+ */
+
+#ifndef MAIN_INCLUDE_SPECIALKEYS_H_
+#define MAIN_INCLUDE_SPECIALKEYS_H_
+
+#include "SDL.h"
+
+class festring;
+
+typedef void (*specialkeyhandler)();
+
+class specialkeys
+{
+ public:
+ enum SKEvent{
+ Filter,
+ ClearStringInput,
+ CopyToClipboard,
+ PasteFromClipboard,
+ FocusedElementHelp,
+ };
+
+ static void init();
+
+ static bool FunctionKeyHandler(SDL_Keycode);
+ static bool ControlKeyHandler(SDL_Keycode);
+ static void AddCtrlOrFuncKeyHandler(SDL_Keycode key, specialkeyhandler Handler);
+
+ static cfestring FilterListQuestion(cfestring What);
+ static void ClearRequest(){Request=-1;}
+
+ static bool ConsumeEvent(SKEvent k){festring fsDummy;return ConsumeEvent(k,fsDummy);}
+ static bool ConsumeEvent(SKEvent,festring& fsInOut);
+ static bool IsConsumingEvent();
+ static bool IsRequestedEvent(SKEvent e);
+ static bool HasEvent(){return Request>=0;}
+
+ private:
+ static int Request;
+};
+
+
+#endif /* MAIN_INCLUDE_SPECIALKEYS_H_ */
diff --git a/FeLib/Include/whandler.h b/FeLib/Include/whandler.h
index c5cadbad3..426e251e7 100644
--- a/FeLib/Include/whandler.h
+++ b/FeLib/Include/whandler.h
@@ -41,8 +41,10 @@ class globalwindowhandler
{
public:
static bool IsKeyPressed(int iSDLScanCode);
- static void ResetKeyTimeout(){SetKeyTimeout(0,'.');}
+ static void ResetKeyTimeout(){SetKeyTimeout(0,iRestWaitKey);}
static void CheckKeyTimeout();
+ static void SuspendKeyTimeout();
+ static void ResumeKeyTimeout();
static truth IsKeyTimeoutEnabled();
static void SetKeyTimeout(int iTimeoutMillis,int iDefaultReturnedKey);
static mouseclick ConsumeMouseEvent();
@@ -73,17 +75,28 @@ class globalwindowhandler
static void Init();
static void SetQuitMessageHandler(truth (*What)()){ QuitMessageHandler = What; }
static ulong UpdateTick() { return Tick = SDL_GetTicks() / 40; }
+ static void SetFunctionKeyHandler(bool (*What)(SDL_Keycode)){ FunctionKeyHandler = What; }
+ static void SetControlKeyHandler(bool (*What)(SDL_Keycode)){ ControlKeyHandler = What; }
#endif
+
#ifdef __DJGPP__
static void Init() { }
static void SetQuitMessageHandler(truth (*)()) { }
static ulong UpdateTick() { return Tick = uclock() * 25 / UCLOCKS_PER_SEC; }
#endif
+
+ const static int iRestWaitKey;
+
private:
#ifdef USE_SDL
+ static int ChkCtrlKey(SDL_Event* Event);
static void ProcessMessage(SDL_Event*);
+ static void ProcessKeyDownMessage(SDL_Event* Event);
+ static void AddKeyToBuffer(int KeyPressed);
static std::vector KeyBuffer;
static truth (*QuitMessageHandler)();
+ static bool (*FunctionKeyHandler)(SDL_Keycode);
+ static bool (*ControlKeyHandler)(SDL_Keycode);
#endif
static truth (*ControlLoop[MAX_CONTROLS])();
static int Controls;
diff --git a/FeLib/Source/config.cpp b/FeLib/Source/config.cpp
index b00084e70..e18f782a8 100644
--- a/FeLib/Source/config.cpp
+++ b/FeLib/Source/config.cpp
@@ -140,6 +140,7 @@ void configsystem::Show(void (*BackGroundDrawer)(),
truth TruthChange = false;
felist List(CONST_S("Which setting do you wish to configure? (* - req. restart)"));
+
List.AddDescription(CONST_S(""));
List.AddDescription(CONST_S("Setting Value"));
@@ -155,7 +156,13 @@ void configsystem::Show(void (*BackGroundDrawer)(),
{
festring Entry = Option[c]->Description;
Entry.Capitalize();
- Entry.Resize(60);
+ int iLim=60;
+ if(Entry.GetSize()>iLim-1){
+ Entry.Resize(iLim-4);
+ Entry<<"...";
+ }else
+ Entry.Resize(iLim-1);
+ Entry<<" "; //space between "columns"
Option[c]->DisplayValue(Entry);
if(fsLastCategory!=Option[c]->fsCategory){
@@ -164,6 +171,7 @@ void configsystem::Show(void (*BackGroundDrawer)(),
}
List.AddEntry(Entry, LIGHT_GRAY);
+ List.SetLastEntryHelp(Option[c]->Description); //TODO show all possible values, and each value could have more details, may require cycling thru them all to get all texts...
}
if(SlaveScreen && ListAttributeInitializer)
diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp
index 72015a3dd..44b801556 100644
--- a/FeLib/Source/feio.cpp
+++ b/FeLib/Source/feio.cpp
@@ -58,10 +58,18 @@
waits for keypress. BitmapEditor is a pointer to function that is
called during every fade tick. */
+bool bInUse=false;
+bool iosystem::IsInUse()
+{
+ return bInUse;
+}
+
void iosystem::TextScreen(cfestring& Text, v2 Disp,
col16 Color, truth GKey, truth Fade,
bitmapeditor BitmapEditor)
{
+ bInUse=true;
+
bitmap Buffer(RES, 0);
Buffer.ActivateFastFlag();
festring::sizetype c;
@@ -110,6 +118,8 @@ void iosystem::TextScreen(cfestring& Text, v2 Disp,
else
GET_KEY();
}
+
+ bInUse=false;
}
/* Returns amount of chars cSF in string sSH */
@@ -335,6 +345,8 @@ int iosystem::StringQuestion(festring& Input,
truth Fade, truth AllowExit,
stringkeyhandler StringKeyHandler)
{
+ bInUse=true;
+
v2 V(RES.X, 10); ///???????????
bitmap BackUp(V, 0);
blitdata B = { &BackUp,
@@ -402,8 +414,10 @@ int iosystem::StringQuestion(festring& Input,
if(!LastKey)
continue;
- if(LastKey == KEY_ESC && AllowExit)
+ if(LastKey == KEY_ESC && AllowExit){
+ bInUse=false;
return ABORTED;
+ }
if(LastKey == KEY_BACK_SPACE)
{
@@ -473,6 +487,7 @@ int iosystem::StringQuestion(festring& Input,
Input.Resize(LastAlpha + 1);
+ bInUse=false;
return NORMAL_EXIT;
}
@@ -484,6 +499,8 @@ int iosystem::StringQuestion(festring& Input,
long iosystem::NumberQuestion(cfestring& Topic, v2 Pos, col16 Color,
truth Fade, truth ReturnZeroOnEsc)
{
+ bInUse=true;
+
v2 V(RES.X, 10); ///???????????
bitmap BackUp(V, 0);
blitdata B = { &BackUp,
@@ -539,8 +556,10 @@ long iosystem::NumberQuestion(cfestring& Topic, v2 Pos, col16 Color,
if(LastKey == KEY_ESC)
{
- if(ReturnZeroOnEsc)
+ if(ReturnZeroOnEsc){
+ bInUse=false;
return 0;
+ }
break;
}
@@ -580,6 +599,7 @@ long iosystem::NumberQuestion(cfestring& Topic, v2 Pos, col16 Color,
static_cast(LastKey));
}
+ bInUse=false;
return atoi(Input.CStr());
}
@@ -600,6 +620,8 @@ long iosystem::ScrollBarQuestion(cfestring& Topic, v2 Pos,
col16 Color2, int LeftKey, int RightKey,
truth Fade, void (*Handler)(long))
{
+ bInUse=true;
+
long BarValue = StartValue;
festring Input;
truth FirstTime = true;
@@ -776,11 +798,15 @@ long iosystem::ScrollBarQuestion(cfestring& Topic, v2 Pos,
Input << char(LastKey);
}
+ bInUse=false;
return BarValue;
}
bool AlertConfirmMsg(const char* cMsg,std::vector vfsCritMsgs = std::vector())
-{//TODO this method could be more global
+{
+ bInUse=true;
+
+ //TODO this method could be more global
//TODO calc all line withs to determine the full popup width to not look bad if overflow
int iLineHeight=20;
v2 v2Border(700,100+(vfsCritMsgs.size()*iLineHeight));
@@ -811,9 +837,12 @@ bool AlertConfirmMsg(const char* cMsg,std::vector vfsCritMsgs = std::v
graphics::BlitDBToScreen(); //as the final blit may be from StretchedBuffer
- if(GET_KEY() == 'y')
+ if(GET_KEY() == 'y'){
+ bInUse=false;
return true;
+ }
+ bInUse=false;
return false;
}
diff --git a/FeLib/Source/felist.cpp b/FeLib/Source/felist.cpp
index 219e585b5..40228e2fc 100644
--- a/FeLib/Source/felist.cpp
+++ b/FeLib/Source/felist.cpp
@@ -12,13 +12,17 @@
#include
#include
+#include
+#include
+#include "feio.h"
#include "felist.h"
#include "graphics.h"
#include "bitmap.h"
#include "whandler.h"
#include "rawbit.h"
#include "save.h"
+#include "specialkeys.h"
#include "festring.h"
#include "dbgmsgproj.h"
@@ -32,6 +36,12 @@ truth felist::isAnyFelistCurrentlyDrawn(){
return FelistCurrentlyDrawn!=NULL;
}
+int ListItemAltPosBackgroundMinY=0;
+void felist::SetListItemAltPosMinY(int iY)
+{
+ ListItemAltPosBackgroundMinY=iY;
+}
+
bool felist::PrepareListItemAltPosBackground(blitdata& rB,bool bAltPosFullBkg){
if(FelistCurrentlyDrawn==NULL)return false;
@@ -45,7 +55,8 @@ bool felist::PrepareListItemAltPosBackground(blitdata& rB,bool bAltPosFullBkg){
// scaled up item pos
rB.Dest.X=FelistCurrentlyDrawn->v2OriginalPos.X;//B.Dest.X=5;
rB.Dest.Y=rB.Src.Y - v2ItemFinalSize.Y/2;
- if(rB.Dest.Y<0)rB.Dest.Y=0;
+ if(rB.Dest.YSelectable)
+ ++Selected;
+
+ --Selected;
+
+ uint pb = Selected - Selected % PageLength;
+ if(PageBegin == pb)
+ JustRedrawEverythingOnce = true;
+ else
+ BackGround.FastBlit(Buffer);
+
+ return pb;
+}
+
+felistentry* RetrieveSelectableEntry(std::vector Entry,uint Selected){
+ uint iSel=0;
+ for(uint i=0;iSelectable)continue;
+
+ if(iSel==Selected)
+ return Entry[i];
+
+ iSel++;
+ }
+ return NULL;
+}
+
+typedef std::map filtermap;
+filtermap FilterMap;
+
+festring felist::GetFilter()
+{
+ festring key = Description[0]->String;
+ filtermap::iterator Iterator = FilterMap.find(key);
+ return Iterator != FilterMap.end() ? Iterator->second : "";
+}
+
+void felist::SetFilter(festring Filter)
+{
+ festring key = Description[0]->String;
+ filtermap::iterator iter = FilterMap.find(key);
+ if(iter!=FilterMap.end())
+ FilterMap.erase(iter);
+ FilterMap.insert(std::make_pair(key,Filter));
+}
+
+void felist::UpdateFilterDesc()
+{
+ festring Filter=GetFilter();
+
+ static festring fsF="[Filter '";
+ festring fsD = fsF+Filter+"']";
+ bool bFound=false;
+ for(int i=0;iString.CStr()).substr(0,fsF.GetSize())==fsF.CStr()){
+ if(Filter.IsEmpty())
+ Description.erase(Description.begin()+i);
+ else
+ Description[i]->String = fsD;
+ bFound=true;
+ break;
+ }
+ }
+ if(!bFound && !Filter.IsEmpty())
+ AddDescription(fsD,YELLOW);
+}
+
+void felist::ApplyFilter()
+{
+ festring Filter=GetFilter();
+
+ UpdateFilterDesc();
+
+ if(!Filter.IsEmpty()){
+ Entry.clear();
+ std::string sFilter=Filter.CStr();DBG1(sFilter);
+ std::transform(sFilter.begin(), sFilter.end(), sFilter.begin(), ::tolower);
+ std::string str;
+ for(int i=0;iSelectable)continue;
+
+ str = EntryBkp[i]->String.CStr();
+ std::transform(str.begin(), str.end(), str.begin(), ::tolower); DBG1(str);
+ if(str.find(sFilter)!=std::string::npos)
+ Entry.push_back(EntryBkp[i]);
+ }
+ DBG3(Filter.CStr(),EntryBkp.size(),Entry.size());
+ if(Entry.empty()){ //filter was invalid
+ Entry=EntryBkp;
+ SetFilter("");
+ UpdateFilterDesc();
+ }
+ }else{
+ if(EntryBkp.size()>0)
+ Entry=EntryBkp;
+ }
+}
+
+void felist::PrepareToReturn()
+{
+ UpdateFilterDesc();
+ Entry=EntryBkp; //to be ready to proper felist::Empty() with deletion
+ EntryBkp.clear();
+}
+
uint felist::Draw()
{
- uint FlagsChk = Flags;
+ EntryBkp=Entry;
+
+ specialkeys::ClearRequest();
+
+ ApplyFilter();
if(Flags & SELECTABLE){
if(PageLength > 26)PageLength=26; //constraint limit from aA to zZ as there is no coded support beyond these keys anyways...
@@ -190,7 +317,7 @@ uint felist::Draw()
* But this is still a dumb guesser, because it considers all entries will have images.
*
* The difficulty is because having a fixed page length, even if the contents of each page may differ,
- * we are unable to precisely calculate how many entries will fit on each page.
+ * we are unable to precisely calculate how many entries will fit on each page. TODO right?
*
* So, opting for the worst case (all are images) is the safest option.
*/
@@ -199,6 +326,76 @@ uint felist::Draw()
}
}
+ for(;;){
+ bool bJustExitTheList=false;
+ uint Return = DrawFiltered(bJustExitTheList);
+ if(bJustExitTheList){
+ PrepareToReturn();
+ return Return;
+ }
+
+ if(Return == ESCAPED || Return == LIST_WAS_EMPTY){ //TODO FELIST_ERROR_BIT?
+ PrepareToReturn();
+ return Return;
+ }
+
+ if(Return == NOTHING_SELECTED){ //special condition if has filter
+ if(!GetFilter().IsEmpty()){
+ ApplyFilter();
+ continue;
+ }else{
+ if(bJustRestoreEntries){
+ UpdateFilterDesc();
+ Entry=EntryBkp;
+ continue;
+ }else{
+ PrepareToReturn();
+ return NOTHING_SELECTED;
+ }
+ }
+ }
+
+ ////////////////////////// something was chosen ///////////////////////
+ DBG3(Return,Entry.size(),EntryBkp.size());
+ if(!GetFilter().IsEmpty()){
+ /**
+ * the filtered index differs from the original index...
+ * the matching key will be the entry description/text
+ */
+
+ felistentry* fleR = Entry[Return];
+
+ int iSelB=0;
+ for(int i=0;iString.CStr());
+ if(!EntryBkp[i]->Selectable)continue;
+
+ if(EntryBkp[i]->String==fleR->String){ //TODO there may have 2 items with identical descriptions tho... but user wouldnt be able to distinguish either right?
+ Return = iSelB; DBG1(Return);
+ break;
+ }
+
+ iSelB++;
+ }
+ }
+
+ PrepareToReturn();
+ return Return;
+ }
+
+ PrepareToReturn();
+ return NOTHING_SELECTED; //currently never reached, safe dummy tho
+}
+
+void felist::ClearFilter()
+{
+ SetFilter("");
+ ApplyFilter();
+}
+
+uint felist::DrawFiltered(bool& bJustExitTheList)
+{
+ uint FlagsChk = Flags;
+
while(Entry.size() && Entry[GetLastEntryIndex()]->String.IsEmpty())
Pop();
@@ -229,7 +426,7 @@ uint felist::Draw()
uint c;
uint Return, Selectables = 0;
- truth JustSelectMoveOnce = false;
+ truth JustRedrawEverythingOnce = false;
for(c = 0; c < Entry.size(); ++c)
if(Entry[c]->Selectable)
@@ -245,7 +442,6 @@ uint felist::Draw()
else
PageBegin = 0;
- bool bSafeScrollToEnd=false; //page per page
bool bWaitKeyUp=false;
bool bClearKeyBufferOnce=false;
bool bInvM = Flags & INVERSE_MODE;
@@ -253,6 +449,9 @@ uint felist::Draw()
v2 v2MousePosPrevious=globalwindowhandler::GetMouseLocation();
globalwindowhandler::ConsumeMouseEvent(); //this call is important to clear the last mouse action outside felist
int iDrawCount=0;
+ festring Filter=GetFilter();
+ festring fsFilterApplyNew=Filter;
+ bool bApplyNewFilter=false;
for(;;)
{
if(FlagsChk != Flags)ABORT("flags changed during felist draw %s %s",std::bitset<16>(FlagsChk).to_string().c_str(), std::bitset<16>(Flags).to_string().c_str());
@@ -261,21 +460,21 @@ uint felist::Draw()
truth LastEntryVisible = DrawPage(Buffer,&v2FinalPageSize,&vEntryRect);DBGLN;
if(FirstDrawNoFade && iDrawCount == 0){
- JustSelectMoveOnce=true;
+ JustRedrawEverythingOnce=true;
}
iDrawCount++;
if(Flags & FADE)
{DBGLN;
- if(JustSelectMoveOnce)
+ if(JustRedrawEverythingOnce)
{DBGLN;
Buffer->FastBlit(DOUBLE_BUFFER);
graphics::BlitDBToScreen();
}else{DBGLN;
- Buffer->FadeToScreen();
+ Buffer->FadeToScreen();DBGLN;
}
- JustSelectMoveOnce = false;
+ JustRedrawEverythingOnce = false;
}else{DBGLN;
if(Buffer != DOUBLE_BUFFER)ABORT("felist non-fade Buffer != DOUBLE_BUFFER");
graphics::BlitDBToScreen();
@@ -292,17 +491,58 @@ uint felist::Draw()
bool bLeftMouseButtonClick=false;
bool bMouseButtonClick=false;
bool bJustRefreshOnce=false;
- if(bSafeScrollToEnd)
- Pressed = bInvM ? KEY_PAGE_UP : KEY_PAGE_DOWN;
for(;;){
/**
* every section here may break the loop and they are prioritized
*/
- ////////////////////////////// scroll to end by-pass
- if(bSafeScrollToEnd)
+ if(
+ specialkeys::ConsumeEvent(specialkeys::Filter,fsFilterApplyNew)
+ ||
+ specialkeys::ConsumeEvent(specialkeys::ClearStringInput,fsFilterApplyNew)
+ ){
+ if(Filter != fsFilterApplyNew){DBGLN;
+ if(fsFilterApplyNew.IsEmpty())
+ bJustRestoreEntries=true;
+ bApplyNewFilter=true;
+ }else{
+ bJustRefreshOnce=true;
+ }
+ break;
+ }else
+ if(specialkeys::IsRequestedEvent(specialkeys::CopyToClipboard)){
+ if(Flags & SELECTABLE){
+ felistentry* fle = RetrieveSelectableEntry(Entry,Selected);
+ if(fle!=NULL)
+ specialkeys::ConsumeEvent(specialkeys::CopyToClipboard,fle->String);
+ }else{
+ specialkeys::ClearRequest();
+ }
+ //TODO copy the entiry list if not selectable? nah...?
+ }else
+ if(specialkeys::IsRequestedEvent(specialkeys::FocusedElementHelp)){
+ festring fs;
+ felistentry* fle = RetrieveSelectableEntry(Entry,Selected);
+ if(fle!=NULL){
+ if(!fle->Help.IsEmpty())
+ fs<Help<<"\n";
+ else
+ fs<String<<"\n";
+ fs<<"\n";
+ }
+ fs<<
+ "[List Help:]\n"
+ " F1 - show this message\n"
+ " Ctrl+F - filter entries\n"
+ " Ctrl+DEL - clear filter\n"
+ " Home/End/PageUp/PageDown - navigate thru pages\n"
+ " ESC - exit the list\n"
+ " SPACE - continue (next page or exit if at last one)\n";
+ specialkeys::ConsumeEvent(specialkeys::FocusedElementHelp,fs);
+ bJustRefreshOnce=true;
break;
+ }
/////////////////////////////////////////// MOUSE ///////////////////////////////////////
v2 v2MousePos = globalwindowhandler::GetMouseLocation();
@@ -358,7 +598,7 @@ uint felist::Draw()
if(bSelChanged){
Selected = iSel; DBG1(iSel);
bJustRefreshOnce=true;
- JustSelectMoveOnce=true;
+ JustRedrawEverythingOnce=true;
break;
}
}
@@ -371,14 +611,23 @@ uint felist::Draw()
if(bClearKeyBufferOnce){
bClearKeyBuffer=true;
bClearKeyBufferOnce=false;
- }
- Pressed = GET_KEY(bClearKeyBuffer);DBG1(Pressed); //see iTimeoutMillis above
+ }DBGLN;
+ Pressed = GET_KEY(bClearKeyBuffer);DBG2(Pressed,DefaultAnswer); //see iTimeoutMillis above
+// if(specialkeys::HasEvent()){DBGLN;
+// bJustRefreshOnce=true;
+// break;
+// }
if(Pressed!=DefaultAnswer)
break;
-
}
DBGLN;
+ if(Pressed == KEY_ESC) // this here grants will be preferred over everything else below
+ {
+ Return = ESCAPED;
+ break;
+ }
+
// if(bMouseHovering && !bMouseButtonClick)
if(bJustRefreshOnce)
continue;
@@ -413,23 +662,11 @@ uint felist::Draw()
PageBegin -= PageLength;
}
else
- JustSelectMoveOnce = true;
+ JustRedrawEverythingOnce = true;
}
else
{
- for(c = 0, Selected = 0; c < Entry.size(); ++c)
- if(Entry[c]->Selectable)
- ++Selected;
-
- --Selected;
-
- if(PageBegin == Selected - Selected % PageLength)
- JustSelectMoveOnce = true;
- else
- {
- BackGround.FastBlit(Buffer);
- PageBegin = Selected - Selected % PageLength;
- }
+ PageBegin = ScrollToLastPage(JustRedrawEverythingOnce, BackGround, Buffer);
}
if(globalwindowhandler::IsLastSDLkeyEventWasKeyUp())
@@ -450,12 +687,12 @@ uint felist::Draw()
PageBegin += PageLength;
}
else
- JustSelectMoveOnce = true;
+ JustRedrawEverythingOnce = true;
}
else
{
if(!PageBegin)
- JustSelectMoveOnce = true;
+ JustRedrawEverythingOnce = true;
else
BackGround.FastBlit(Buffer);
@@ -476,7 +713,11 @@ uint felist::Draw()
break;
}
- if(Pressed == KEY_ESC)
+ if(bApplyNewFilter){DBGLN;
+ break;
+ }
+
+ if(Pressed == KEY_ESC) // this here grants will be preferred over everything else below
{
Return = ESCAPED;
break;
@@ -493,13 +734,14 @@ uint felist::Draw()
if(!bNav && Pressed == KEY_HOME)bNav=true;
if(!bNav && Pressed == KEY_END)bNav=true; //TODO ? END key usage is getting complicated, disabled for now:
- if(!bNav) {DBGLN;
- if(Pressed == KEY_SPACE) //to work stictly as on the help info
- if(bInvM ? PageBegin==0 : LastEntryVisible){DBGLN;
- Return = NOTHING_SELECTED;
- break;
- }
- } else {DBGLN;
+ if(Pressed == KEY_SPACE) //to work stictly as on the help info
+ if(bInvM ? PageBegin==0 : LastEntryVisible){DBGLN;
+ bJustExitTheList=true;
+ Return = NOTHING_SELECTED;
+ break;
+ }
+
+ if(bNav) {
BackGround.FastBlit(Buffer);
int iDir = 1;
@@ -509,22 +751,18 @@ uint felist::Draw()
iDir *= -1;
int iPB = PageBegin + iDir*PageLength;DBG1(iPB);
- if(iPB<0) //PageBegin is uint ...
+ if(iPB<0) //BEWARE!!! PageBegin is uint ...
iPB=0;
/**
* overriders
* obs.: pgdn and space are default "advance page" action
*/
- if(LastEntryVisible && bSafeScrollToEnd){DBGLN;
- bSafeScrollToEnd=false;
- Selected = Selectables-1;
- continue; //do nothing
- }
if(bInvM ? Pressed == KEY_END : Pressed == KEY_HOME) // go to first
iPB=0;
+ bool bSelLast=false;
if(bInvM ? Pressed == KEY_HOME : Pressed == KEY_END){DBGLN; // go to last
if(Entry.size()<=PageLength){DBGLN; //only one page
Selected = Selectables-1;
@@ -536,23 +774,24 @@ uint felist::Draw()
continue; //do nothing
}
- bSafeScrollToEnd=true; // will just page down once, as this is the default action, otherwise should `continue;`
+ DBG6("Before",iPB,Selectables,Selected,PageLength,Entry.size());
+ iPB = ScrollToLastPage(JustRedrawEverythingOnce, BackGround, Buffer); DBG6("After",iPB,Selectables,Selected,PageLength,Entry.size());
+ bSelLast=true;
}
// fail safe LAST check
- if(iPB >= Selectables){ DBG3("how it happened?",iPB,Selectables);
+ if(iPB >= Selectables){ DBG6("HowItHappened?",iPB,Selectables,Selected,PageLength,Entry.size());
continue; //do nothing
}
// apply
- PageBegin = iPB;
-
- DBG3(PageBegin,Pressed,iDir);
+ PageBegin = iPB; DBG3(PageBegin,Pressed,iDir);
- if(Flags & SELECTABLE)
- Selected = PageBegin;
+ if(!bSelLast)
+ if(Flags & SELECTABLE)
+ Selected = PageBegin;
}
- }
+ };DBGLN;
if(!(Flags & FADE))
{
@@ -579,6 +818,12 @@ uint felist::Draw()
#endif
globalwindowhandler::ResetKeyTimeout();
+
+ if(bApplyNewFilter){
+ SetFilter(fsFilterApplyNew);
+ return NOTHING_SELECTED;
+ }
+
return Return;
}
@@ -590,7 +835,7 @@ bool felist::IsEntryDrawingAtValidPos(bitmap* Buffer,v2 pos){
}
truth felist::DrawPage(bitmap* Buffer, v2* pv2FinalPageSize, std::vector* pvEntryRect) const
-{
+{ DBGSV2(Pos);
uint LastFillBottom = Pos.Y + 23 + Description.size() * 10;
DrawDescription(Buffer);
@@ -722,8 +967,11 @@ truth felist::DrawPage(bitmap* Buffer, v2* pv2FinalPageSize, std::vectorFill(iTLX, LastFillBottom, iWidth, iHeight=30, BackColor);
+ int iPg = (PageBegin/PageLength)+1;
+ int iPgTot = Entry.size()/PageLength + (Entry.size()%PageLength>0 ? 1 : 0);
+ if(iPgTot==0)iPgTot=1;
FONT->Printf(Buffer, v2(Pos.X + 13, LastFillBottom + 10), WHITE,
- "- Press PgUp/PgDn/Home/End or SPACE to continue, ESC to exit -");
+ "- Page %d/%d (Press F1 to show help info) -",iPg,iPgTot);
LastFillBottom += 30;
}
else
@@ -862,6 +1110,11 @@ void felist::AddEntry(cfestring& Str, col16 Color,
}
}
+void felist::SetLastEntryHelp(cfestring Help)
+{
+ Entry[Entry.size()-1]->Help=Help;
+}
+
void felist::Save(outputfile& SaveFile) const
{
SaveFile << Entry << Maximum << Selected;
diff --git a/FeLib/Source/graphics.cpp b/FeLib/Source/graphics.cpp
index 841105049..84dcef848 100644
--- a/FeLib/Source/graphics.cpp
+++ b/FeLib/Source/graphics.cpp
@@ -586,11 +586,10 @@ struct drawaboveentry{
const char* desc; //to help on development
};
std::vector vDrawabove;
-void graphics::DrawAboveAll(bitmap* bmpDest)
+void graphics::DrawAboveAll(bitmap* bmpBuffer)
{
- for(int i=0;i
+#include
+#include
+#include
+#include
+#include
+
+#include "bitmap.h"
+#include "feio.h"
+#include "error.h"
+#include "festring.h"
+#include "graphics.h"
+#include "rawbit.h"
+#include "specialkeys.h"
+#include "whandler.h"
+
+#include "dbgmsgproj.h"
+
+/**
+ * They modify current behavior of anything that accepts such modifications,
+ * or
+ * they may override whatever is happening with something else.
+ */
+
+//#undef REQ
+//#define REQ(name) \
+// bool b##name##Request=false; \
+// void specialkeys::Clear##name##Request(){b##name##Requested=false;}
+//REQ(Filter);
+//REQ(FocusedElementHelp);
+//REQ(CopyToClipboard);
+//REQ(PasteFromClipboard);
+
+cfestring specialkeys::FilterListQuestion(cfestring what)
+{
+ festring What=what;
+
+ v2 pos = v2(16, 6);
+ static festring Topic = "Type this list filter:";
+
+ int R = iosystem::StringQuestion(What, Topic, pos, WHITE, 0, 30, false /*TODO !bGameIsRunning*/, true, NULL);
+
+ /**
+ * TODO
+ * clear the filter text using the background!
+ * needed when pressing ESC or ENTER and not changing the filter!
+ * tip: igraph::BlitBackGround(pos, v2(RES.X, 23));
+ * also, could may be copy the background from doublebuffer and just paste it back after....
+ */
+
+ // cheap workaround to not look too bad (like input was not accepted) at least
+ // DOUBLE_BUFFER->Fill(pos,v2(RES.X, 23),BLACK);
+ FONT->Printf(DOUBLE_BUFFER, pos, BLACK, "%s", Topic.CStr());
+ FONT->Printf(DOUBLE_BUFFER, v2(pos.X, pos.Y + 10), BLACK, "%s_", What.CStr());
+
+ if(R == NORMAL_EXIT){ DBG1(What.CStr());
+ return What;
+ }
+
+ if(R == ABORTED)
+ return what;
+
+ return cfestring();
+}
+
+bool specialkeys::IsRequestedEvent(SKEvent e)
+{
+ return (e==Request);
+}
+
+std::vector afsHelpDialog;
+void DrawHelpDialog(bitmap* Buffer) //TODO this kind'o message should be more global to be easier to be used...
+{ //TODO create a buffer to not re-draw every loop... unless want to animate it...
+ if(afsHelpDialog.size()==0)return;
+
+ int iFontLetterWidth=8;
+ int iLH=15;
+
+ v2 v2Border;
+ v2Border.Y=afsHelpDialog.size()*iLH+iLH*2;
+ for(int i=0;iFill(v2TL,v2Border,BLACK);
+ graphics::DrawRectangleOutlineAround(Buffer, v2TL, v2Border, DARK_GRAY, true);
+
+ v2TL+=v2(iLH,iLH);
+
+ for(int i=0;iPrintf(Buffer, v2(v2TL.X,v2TL.Y+i*iLH), WHITE, "%s", afsHelpDialog[i].CStr());
+}
+
+bool bConsumingEvent=false;
+bool specialkeys::IsConsumingEvent()
+{
+ return bConsumingEvent;
+}
+
+int specialkeys::Request=-1;
+bool specialkeys::ConsumeEvent(SKEvent e,festring& fsInOut){DBGLN;
+ if(!IsRequestedEvent(e))
+ return false;
+
+ bConsumingEvent=true;
+
+ switch(e){
+ case ClearStringInput:{
+ fsInOut.Empty();
+ Request=-1;
+ };break;
+
+ case Filter:{DBGLN;
+ globalwindowhandler::SuspendKeyTimeout();
+ fsInOut=FilterListQuestion(fsInOut);
+ globalwindowhandler::ResumeKeyTimeout();
+ // bFilterRequest=false;
+ Request=-1;
+ };break;
+
+ case CopyToClipboard:{DBGLN;
+ #ifdef UNIX //TODO for MACOSX can use `pbcopy` see https://github.com/Attnam/ivan/pull/458#discussion_r212823516
+ /* TODO
+ * the executable OS command may allow, thru the input text, any OS commands and that is not good.
+ * ex.: echo just needs to be broken like: echo -n ''`anyOtherOSCommand`'' |xclip -i //where the input string is "'`anyOtherOSCommand`'"
+ * so this needs to be done in some other way...
+ std::ostringstream osStkCmd;
+ // this will execute an OS command
+ osStkCmd<<"echo -n '"<0)
+ fsInOut = buf;
+ pclose(pipeFile);
+ }else{
+ DBG2("unable to execute popen() with cmd: ",osStkCmd.str());
+ }
+ #endif
+ //TODO windows (can be hardcoded)
+ };break;
+
+ case FocusedElementHelp:{DBGLN;
+ globalwindowhandler::SuspendKeyTimeout();
+
+ std::stringstream ss(fsInOut.CStr());
+ std::string line;
+ while(std::getline(ss,line,'\n'))
+ afsHelpDialog.push_back(festring(line.c_str()));
+
+ graphics::BlitDBToScreen();DBGLN;
+
+ GET_KEY(true);DBGLN;
+
+ afsHelpDialog.clear();
+
+ globalwindowhandler::ResumeKeyTimeout();
+
+ Request=-1; //it is com
+ };break;
+ }
+
+ bConsumingEvent=false;
+
+ return true;
+}
+
+void specialkeys::init()
+{
+ globalwindowhandler::SetFunctionKeyHandler(specialkeys::FunctionKeyHandler);
+ globalwindowhandler::SetControlKeyHandler(specialkeys::ControlKeyHandler);
+
+ graphics::AddDrawAboveAll(&DrawHelpDialog,90000,"HelpDialog");
+}
+
+typedef std::map ckhmap;
+ckhmap CkhMap;
+
+bool specialkeys::FunctionKeyHandler(SDL_Keycode key)
+{DBGLN;
+ switch(key){ //TODO how to not use SDLK_ keys here??? shouldnt anyway????
+ case SDLK_F1:DBGLN;
+ Request=FocusedElementHelp;
+ return true;
+ default:
+ ckhmap::iterator Iterator = CkhMap.find(key);
+ if(Iterator != CkhMap.end()){
+ Iterator->second();
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+/**
+ * add Function or Ctrl+ key handler
+ */
+void specialkeys::AddCtrlOrFuncKeyHandler(SDL_Keycode key, specialkeyhandler Handler)
+{
+ ckhmap::iterator Iterator = CkhMap.find(key);
+ if(Iterator != CkhMap.end())
+ ABORT("control key handler already set for key %d",key);
+
+ CkhMap.insert(std::make_pair(key,Handler));
+}
+
+/**
+ * Ctrl+FunctionKey is here tho
+ */
+bool specialkeys::ControlKeyHandler(SDL_Keycode key)
+{
+ switch(key){ //TODO use SDLK_ keys?
+ case 'f':
+ Request=Filter;
+ return true;
+ case 'c':
+ Request=CopyToClipboard;
+ return true;
+ case 'v':
+ Request=PasteFromClipboard;
+ return true;
+ case SDLK_DELETE:
+ Request=ClearStringInput;
+ return true;
+ default:
+ ckhmap::iterator Iterator = CkhMap.find(key);
+ if(Iterator != CkhMap.end()){
+ Iterator->second();
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
diff --git a/FeLib/Source/whandler.cpp b/FeLib/Source/whandler.cpp
index 1ca48e8db..63522da18 100644
--- a/FeLib/Source/whandler.cpp
+++ b/FeLib/Source/whandler.cpp
@@ -140,6 +140,8 @@ int globalwindowhandler::ReadKey()
std::vector globalwindowhandler::KeyBuffer;
truth (*globalwindowhandler::QuitMessageHandler)() = 0;
+bool (*globalwindowhandler::FunctionKeyHandler)(SDL_Keycode) = 0;
+bool (*globalwindowhandler::ControlKeyHandler)(SDL_Keycode) = 0;
void globalwindowhandler::Init()
{
@@ -273,8 +275,10 @@ int FrameSkipOrDraw(){ //TODO could this be simplified?
}
}
+const int globalwindowhandler::iRestWaitKey = '.';
+
int iTimeoutDelay=0; // must init with 0
-int iTimeoutDefaultKey;
+int iTimeoutDefaultKey = globalwindowhandler::iRestWaitKey;
long keyTimeoutRequestedAt;
/**
* This is intended to remain active ONLY until the user hits any key.
@@ -304,6 +308,16 @@ void globalwindowhandler::CheckKeyTimeout()
}
}
}
+int iTimeoutDelayBkp=0;
+void globalwindowhandler::SuspendKeyTimeout()
+{
+ iTimeoutDelayBkp=iTimeoutDelay;
+ iTimeoutDelay=0;
+}
+void globalwindowhandler::ResumeKeyTimeout()
+{
+ iTimeoutDelay=iTimeoutDelayBkp;
+}
float globalwindowhandler::GetFPS(bool bInsta){
if(bInsta)return fInstaFPS;
@@ -365,33 +379,28 @@ int globalwindowhandler::GetKey(truth EmptyBuffer)
}
else
{
- bool bHasEvents=PollEvents(&Event)>0;
-// bool bHasEvents=false;
-// while(SDL_PollEvent(&Event)){
-// ProcessMessage(&Event);
-// bHasEvents=true;
-// }
-
- if(!bHasEvents)
- {
- bool bPlay=true;
-
+ bool bHasFocus=false;
#if SDL_MAJOR_VERSION == 1
- if(bPlay && !(SDL_GetAppState() & SDL_APPACTIVE))
+ bHasFocus = SDL_GetAppState() & SDL_APPACTIVE;
#else
- if(bPlay &&
- !( SDL_GetWindowFlags(graphics::GetWindow()) & (SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS) )
- )
+ bHasFocus = SDL_GetWindowFlags(graphics::GetWindow()) & (SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
#endif
- if(!playInBackground)
- bPlay=false;
- if(bPlay && Controls==0)
- bPlay=false;
+ bool bPlay=true;
+
+ if(bPlay && !bHasFocus && !playInBackground)
+ bPlay=false;
+
+ if(bPlay && Controls==0)
+ bPlay=false;
- if(bPlay && !ControlLoopsEnabled)
- bPlay=false;
+ if(bPlay && !ControlLoopsEnabled)
+ bPlay=false;
+
+ bool bHasEvents=PollEvents(&Event)>0;
+ if(!bHasEvents)
+ {
if(bPlay)
{
static ulong LastTick = 0;
@@ -425,8 +434,13 @@ int globalwindowhandler::GetKey(truth EmptyBuffer)
}
else
{
- SDL_WaitEvent(&Event);
- ProcessMessage(&Event);
+ if(bHasFocus){
+ iDelayMS=1000/30; //30 FPS on main menu just to not use too much CPU there. If one day it is animated, lower this properly.
+ SDL_Delay(iDelayMS);
+ }else{
+ SDL_WaitEvent(&Event);DBGLN;
+ ProcessMessage(&Event);DBGLN;
+ }
}
}
}
@@ -551,16 +565,185 @@ mouseclick globalwindowhandler::ConsumeMouseEvent() //TODO buffer it?
return mcR;
}
-void globalwindowhandler::ProcessMessage(SDL_Event* Event)
+int globalwindowhandler::ChkCtrlKey(SDL_Event* Event)
{
+ if(Event->key.keysym.mod & KMOD_CTRL){ //if CTRL is pressed, user expects something else than the normal key, therefore not permissive
+ if(ControlKeyHandler!=NULL)
+ ControlKeyHandler(Event->key.keysym.sym);
+ return iRestWaitKey; //gum TODO 0 should suffice one day...
+ }DBGLN;
+
+ return Event->key.keysym.sym;
+}
+
+void globalwindowhandler::ProcessKeyDownMessage(SDL_Event* Event)
+{DBG4(Event->key.keysym.sym,Event->text.text[0],Event->key.keysym.mod & KMOD_ALT,Event->key.keysym.mod & KMOD_CTRL);
+
+ bLastSDLkeyEventIsKeyUp=false;
+
+ /**
+ * Events are splitted between SDL_KEYDOWN and SDL_TEXTINPUT.
+ *
+ * All managed events must be explicited,
+ * so, all keyDown events that will be modified must be handled here,
+ * all other non modified keyDown events will be handled by SDL_TEXTINPUT event type outside here.
+ *
+ * More modifiers also means higher priority.
+ *
+ * if one or more modifiers are pressed,
+ * user expects something else than the normal key,
+ * therefore wont fill the key buffer
+ *
+ * Non handled ctrl+alt+... or ctrl+... or alt+... will be ignored.
+ * Tho, they may be overriden by the OS and never reach here...
+ */
+
+ if((Event->key.keysym.mod & KMOD_CTRL) && (Event->key.keysym.mod & KMOD_ALT)){
+ switch(Event->key.keysym.sym)
+ {
+ case SDLK_e:
+ /**
+ * TODO
+ * exemplify where this is or can be ever used as tests provided no results on Linux,
+ * is it the windows Explorer key? if so #ifdef WIN32 should be used...
+ */
+ AddKeyToBuffer('\177');
+ break;
+ }
+ return;
+ }
+
+ if(Event->key.keysym.mod & KMOD_CTRL){
+ if(ControlKeyHandler!=NULL) //this one was completely externalized
+ ControlKeyHandler(Event->key.keysym.sym);
+ return;
+ }else
+ if(Event->key.keysym.mod & KMOD_ALT){
+ switch(Event->key.keysym.sym)
+ {
+ case SDLK_RETURN:
+ case SDLK_KP_ENTER:
+ graphics::SwitchMode();
+ break;
+ }
+ return;
+ }
+
+ // other special non buffered keys
+ switch(Event->key.keysym.sym)
+ {
+ case SDLK_F1: case SDLK_F2: case SDLK_F3: case SDLK_F4: case SDLK_F5:
+ case SDLK_F6: case SDLK_F7: case SDLK_F8: case SDLK_F9: case SDLK_F10:
+ case SDLK_F11: case SDLK_F12: case SDLK_F13: case SDLK_F14: case SDLK_F15:
+ case SDLK_F16: case SDLK_F17: case SDLK_F18: case SDLK_F19: case SDLK_F20:
+ case SDLK_F21: case SDLK_F22: case SDLK_F23: case SDLK_F24:
+ if(FunctionKeyHandler!=NULL)
+ FunctionKeyHandler(Event->key.keysym.sym);
+ return; //no buffer
+
+ case SDLK_SYSREQ:
+ case SDLK_PRINTSCREEN:
+ if(!ScrshotDirectoryName.IsEmpty())
+ DOUBLE_BUFFER->Save(ScrshotNameHandler());
+ return; //no buffer
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////// MODIFIED KEY BUFFER ///////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////////////////////////
int KeyPressed = 0;
+ switch(Event->key.keysym.sym)
+ {
+ case SDLK_RETURN:
+ case SDLK_KP_ENTER:
+ // both SDL keys are mixed into KEY_ENTER
+ KeyPressed = KEY_ENTER; //TODO SDL1? old comment tip or deadCode: Event->key.keysym.unicode;
+ break;
+
+ case SDLK_DOWN:
+ case SDLK_KP_2:
+ KeyPressed = KEY_DOWN + 0xE000;
+ break;
+
+ case SDLK_UP:
+ case SDLK_KP_8:
+ KeyPressed = KEY_UP + 0xE000;
+ break;
+
+ case SDLK_RIGHT:
+ case SDLK_KP_6:
+ KeyPressed = KEY_RIGHT + 0xE000;
+ break;
+
+ case SDLK_LEFT:
+ case SDLK_KP_4:
+ KeyPressed = KEY_LEFT + 0xE000;
+ break;
+
+ case SDLK_HOME:
+ case SDLK_KP_7:
+ KeyPressed = KEY_HOME + 0xE000;
+ break;
+
+ case SDLK_END:
+ case SDLK_KP_1:
+ KeyPressed = KEY_END + 0xE000;
+ break;
+ case SDLK_PAGEUP:
+ case SDLK_KP_9:
+ KeyPressed = KEY_PAGE_UP + 0xE000;
+ break;
+
+ case SDLK_KP_3:
+ case SDLK_PAGEDOWN:
+ KeyPressed = KEY_PAGE_DOWN + 0xE000;
+ break;
+
+ case SDLK_KP_5:
+ KeyPressed = iRestWaitKey;
+ break;
+
+#if SDL_MAJOR_VERSION == 2 //TODO there is no ESC on SDL1??? but does SDL1 still compiles? anyone uses it yet??? the same question about DJGPP...
+ case SDLK_ESCAPE:
+ case SDLK_BACKSPACE:
+ KeyPressed = Event->key.keysym.sym;
+ break;
+#endif
#if SDL_MAJOR_VERSION == 1
- switch(Event->active.type)
+ default:
+ KeyPressed = Event->key.keysym.unicode;
+
+ if(!KeyPressed)
+ return;
+#endif
+ }
+ AddKeyToBuffer(KeyPressed);
+}
+
+/**
+ * buffer of yet non processed commands/textInput
+ */
+void globalwindowhandler::AddKeyToBuffer(int KeyPressed)
+{ DBG1(KeyPressed);
+ if(KeyPressed==0)return;
+
+ if( std::find(KeyBuffer.begin(), KeyBuffer.end(), KeyPressed) == KeyBuffer.end() ) //prevent dups TODO because of fast key-repeat OS feature? should be the last key on buffer only then
+ KeyBuffer.push_back(KeyPressed);
+}
+
+void globalwindowhandler::ProcessMessage(SDL_Event* Event)
+{
+ Uint32 type;
+#if SDL_MAJOR_VERSION == 1
+ type=(Event->active.type);
#else
- switch(Event->type)
+ type=(Event->type);
#endif
+
+ switch(type)
{
+
#if SDL_MAJOR_VERSION == 1
case SDL_VIDEOEXPOSE:
graphics::BlitDBToScreen();
@@ -576,10 +759,10 @@ void globalwindowhandler::ProcessMessage(SDL_Event* Event)
}
#endif
break;
+
case SDL_QUIT:
if(!QuitMessageHandler || QuitMessageHandler())
exit(0);
-
return;
case SDL_MOUSEBUTTONUP:
@@ -589,104 +772,24 @@ void globalwindowhandler::ProcessMessage(SDL_Event* Event)
mc.pos.Y=Event->button.y;
}
break;
+
case SDL_MOUSEWHEEL:
mc.wheelY = Event->wheel.y;
break;
- case SDL_KEYUP:
- bLastSDLkeyEventIsKeyUp=true;
- break;
- case SDL_KEYDOWN:
- bLastSDLkeyEventIsKeyUp=false;
- switch(Event->key.keysym.sym)
- {
- case SDLK_RETURN:
- case SDLK_KP_ENTER:
- if(Event->key.keysym.mod & KMOD_ALT)
- {
- graphics::SwitchMode();
- return;
- }
- else
- KeyPressed = KEY_ENTER; //Event->key.keysym.unicode;
-
- break;
- case SDLK_DOWN:
- case SDLK_KP_2:
- KeyPressed = KEY_DOWN + 0xE000;
- break;
- case SDLK_UP:
- case SDLK_KP_8:
- KeyPressed = KEY_UP + 0xE000;
- break;
- case SDLK_RIGHT:
- case SDLK_KP_6:
- KeyPressed = KEY_RIGHT + 0xE000;
- break;
- case SDLK_LEFT:
- case SDLK_KP_4:
- KeyPressed = KEY_LEFT + 0xE000;
- break;
- case SDLK_HOME:
- case SDLK_KP_7:
- KeyPressed = KEY_HOME + 0xE000;
- break;
- case SDLK_END:
- case SDLK_KP_1:
- KeyPressed = KEY_END + 0xE000;
- break;
- case SDLK_PAGEUP:
- case SDLK_KP_9:
- KeyPressed = KEY_PAGE_UP + 0xE000;
- break;
- case SDLK_KP_3:
- case SDLK_PAGEDOWN:
- KeyPressed = KEY_PAGE_DOWN + 0xE000;
- break;
- case SDLK_KP_5:
- KeyPressed = '.';
- break;
- case SDLK_SYSREQ:
- case SDLK_PRINTSCREEN:
- if(!ScrshotDirectoryName.IsEmpty())
- {
- DOUBLE_BUFFER->Save(ScrshotNameHandler());
- }
- return;
-#if SDL_MAJOR_VERSION == 2
- /* event are now splitted between SDL_KEYDOWN and SDL_TEXTINPUT,
- all managed events must be explicited */
- case SDLK_ESCAPE:
- case SDLK_BACKSPACE:
- KeyPressed = Event->key.keysym.sym;
- break;
+#if SDL_MAJOR_VERSION == 2 //BEFORE key up or down
+ case SDL_TEXTINPUT: DBG2(Event->key.keysym.sym,Event->text.text[0]);
+ AddKeyToBuffer(Event->text.text[0]);
+ break;
#endif
- case SDLK_e:
- if(Event->key.keysym.mod & KMOD_ALT
- && (Event->key.keysym.mod & KMOD_LCTRL
- || Event->key.keysym.mod & KMOD_RCTRL))
- {
- KeyPressed = '\177';
- break;
- }
- default:
-#if SDL_MAJOR_VERSION == 1
- KeyPressed = Event->key.keysym.unicode;
-#endif
+ case SDL_KEYUP: DBGLN;
+ bLastSDLkeyEventIsKeyUp=true;
+ break;
- if(!KeyPressed)
- return;
- }
- if( std::find(KeyBuffer.begin(), KeyBuffer.end(), KeyPressed) == KeyBuffer.end() )
- KeyBuffer.push_back(KeyPressed);
- break;
-#if SDL_MAJOR_VERSION == 2
- case SDL_TEXTINPUT:
- KeyPressed = Event->text.text[0];
- if( std::find(KeyBuffer.begin(), KeyBuffer.end(), KeyPressed) == KeyBuffer.end() )
- KeyBuffer.push_back(KeyPressed);
-#endif
+ case SDL_KEYDOWN: DBGLN;
+ ProcessKeyDownMessage(Event);
+ break;
}
}
diff --git a/INSTALL b/INSTALL
index 091dd02f1..0a883f333 100644
--- a/INSTALL
+++ b/INSTALL
@@ -29,7 +29,7 @@ To install IVAN to a custom prefix, pass the additional flag
(In particular, simply doing `make DESTDIR=/your/prefix/path install`
doesn't work because IVAN needs the prefix information at build-time.)
-If config options toggle is too fast, you can add this flag -DFELIST_WAITKEYUP,
+If config options toggle is too fast, you can add this flag '-DFELIST_WAITKEYUP',
like this: CMAKE_CXX_FLAGS="-DFELIST_WAITKEYUP -DWIZARD" (you may chose what
flags to add independently of each other).
diff --git a/Main/Include/actions.h b/Main/Include/actions.h
index 88313f8f3..505778b34 100644
--- a/Main/Include/actions.h
+++ b/Main/Include/actions.h
@@ -104,14 +104,18 @@ ACTION(go, action)
virtual void Handle();
int GetDirection() const { return Direction; }
void SetDirection(int What) { Direction = What; }
+ void SetDirectionFromRoute();
+ void SetRoute(std::vector What){RouteGoOn=What;};
truth IsWalkingInOpen() const { return WalkingInOpen; }
void SetIsWalkingInOpen(truth What) { WalkingInOpen = What; }
+ bool IsRouteMode(){return RouteGoOn.size()>0;}
virtual truth TryDisplace();
virtual cchar* GetDescription() const;
virtual truth ShowEnvironment() const { return false; }
protected:
int Direction;
truth WalkingInOpen;
+ std::vector RouteGoOn;
};
ACTION(study, action)
diff --git a/Main/Include/bugworkaround.h b/Main/Include/bugworkaround.h
index 095ac9815..0849644c0 100644
--- a/Main/Include/bugworkaround.h
+++ b/Main/Include/bugworkaround.h
@@ -1,3 +1,15 @@
+/*
+ *
+ * Iter Vehemens ad Necem (IVAN)
+ * Copyright (C) Timo Kiviluoto
+ * Released under the GNU General
+ * Public License
+ *
+ * See LICENSING which should be included
+ * along with this file for more details
+ *
+ */
+
#ifndef MAIN_INCLUDE_BUGWORKAROUND_H_
#define MAIN_INCLUDE_BUGWORKAROUND_H_
@@ -6,31 +18,42 @@ struct bugWorkaroundDupPlayerCharItem{
character* Char;
item* it;
};
-class bugWorkaroundDupPlayer{
+class bugfixdp{
public:
- static character* BugWorkaroundDupPlayer(character* CharAsked, v2 v2AskedPos);
+ static void init();
+ static character* ValidatePlayerAt(square* sqr);
+ static bool IsFixing();
+
private:
+ static character* BugWorkaroundDupPlayer();
+ static void DevConsCmd(std::string strCmdParams);
+
static void GatherAllItemInLevel();
- static void ItemWork(character* Char, item* it, bool bFix, const char* cInfo, std::vector- * pvItem,bool bSendToHell);
+ static bool ItemWork(character* Char, item* it, bool bFix, const char* cInfo, std::vector
- * pvItem,bool bSendToHell);
static void FixPlayerDupInv(character* CharChk);
- static void CharEquipmentsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
- static void CharInventoryWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
- static void CharBodypartsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
- static void CharAllItemsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
+ static int TrapsWork();
+ static int CharEquipmentsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
+ static int CharInventoryWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
+ static int CharBodypartsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
+ static int CharAllItemsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem=NULL);
static void CharAllItemsInfo(character* CharAsked);
static void CharAllItemsCollect(character* CharAsked,std::vector
- * pvItem);
+ static character* FindByPlayerID1(v2 ReqPosL,bool bAndFixIt);
+ static std::vector FindByPlayerFlag();
+ static std::vector FindCharactersOnLevel(bool bOnlyPlayers=false);
static bool ScanLevelForCharactersAndItemsWork(item*, bool, bool, std::vector*);
static void CollectAllItemsOnLevel(std::vector
- * pvAllItemsOnLevel);
static void CollectAllCharactersOnLevel(std::vector* pvCharsOnLevel);
static void CollectAllCharactersAndItemsOnLevel(std::vector* pvAllCharAndOrItemsInLevel);
static bool FindDupItemOnLevel(item* itWork, bool bIgnoreBodyParts, bool bAbortOnMultiples);
+ static void ValidateFullLevel();
+ static void DupPlayerFix(character*);
- static void AlertConfirmFixMsg(const char* cMsg, bool bAbortIfNot);
-// static bool isItemOnVector(std::vector
- * pv, item* e){return (std::find(pv->begin(), pv->end(), e) != pv->end());}
-// static bool isCharOnVector(std::vector* pv, character* e){return (std::find(pv->begin(), pv->end(), e) != pv->end());}
- static bool Accepted;
+ static void DrawAlertConfirmFix(bitmap* Buffer);
+ static bool AlertConfirmFixMsg(const char* cMsg);
+ static bool IsAlertConfirmFixMsgDraw();
};
diff --git a/Main/Include/char.h b/Main/Include/char.h
index f8a362596..61f69ff2b 100644
--- a/Main/Include/char.h
+++ b/Main/Include/char.h
@@ -421,6 +421,7 @@ class character : public entity, public id
virtual void SwitchToDig(item*, v2) { }
virtual void SetRightWielded(item*) { }
virtual void SetLeftWielded(item*) { }
+ truth IsAboveUsefulItem();
void GoOn(go*, truth = false);
virtual truth CheckKick() const;
virtual int OpenMultiplier() const { return 2; }
diff --git a/Main/Include/command.h b/Main/Include/command.h
index e83a229a7..1e69478c6 100644
--- a/Main/Include/command.h
+++ b/Main/Include/command.h
@@ -46,8 +46,11 @@ class commandsystem
static void SaveSwapWeapons(outputfile& SaveFile);
static void LoadSwapWeapons(inputfile& SaveFile);
static void ClearSwapWeapons();
+ static std::vector GetRouteGoOnCopy();
private:
static truth Apply(character*);
+ static truth ApplyWork(character* Char,item* itOverride=NULL);
+ static truth ApplyAgain(character* Char);
static truth Close(character*);
static truth Eat(character*);
static truth Drink(character*);
@@ -83,6 +86,7 @@ class commandsystem
static truth Rest(character*);
static truth Sit(character*);
static truth ShowMap(character*);
+ static truth ShowMapWork(character* Char,v2* pv2ChoseLocation=NULL);
static truth Go(character*);
static truth ShowConfigScreen(character*);
static truth ScrollMessagesDown(character*);
@@ -110,6 +114,8 @@ class commandsystem
static truth LevelTeleport(character*);
static truth Possess(character*);
static truth Polymorph(character*);
+#else
+ static truth DevConsCmd(character* Char);
#endif
static truth ToggleRunning(character*);
static truth IssueCommand(character*);
diff --git a/Main/Include/confdef.h b/Main/Include/confdef.h
index 0c64a0cb5..2628b94b0 100644
--- a/Main/Include/confdef.h
+++ b/Main/Include/confdef.h
@@ -703,6 +703,9 @@
#define BIRCH 22
#define TEAK 23
#define DWARF_BIRCH 24
+#define FORGE 26
+#define FURNACE 27
+#define WORK_BENCH 32
#define SNOW_BOULDER 4
diff --git a/Main/Include/definesvalidator.h b/Main/Include/definesvalidator.h
index c26fb6fb0..d0306b671 100644
--- a/Main/Include/definesvalidator.h
+++ b/Main/Include/definesvalidator.h
@@ -8,8 +8,26 @@
#ifndef _DEFINESVALIDATOR_H_
#define _DEFINESVALIDATOR_H_
-class definesvalidator{ public: static void Validate() {
-
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "error.h"
+
+class definesvalidator{
+
+ public:
+ static void init();
+ static void DevConsCmd(std::string);
+ static void GenerateDefinesValidator(std::string);
+
+ static void Validate() {
std::stringstream ssErrors;
std::bitset<32> bsA, bsB;
@@ -4534,6 +4552,14 @@ class definesvalidator{ public: static void Validate() {
#endif
+#ifdef INGOT // DO NOT MODIFY!
+ bsA = 2;
+ bsB = INGOT;
+ if(bsA!=bsB)
+ ssErrors << "Defined INGOT with value 2 from .dat file mismatches hardcoded c++ define value of " << INGOT << "!" << std::endl;
+#endif
+
+
#ifdef INK // DO NOT MODIFY!
bsA = 16396;
bsB = INK;
@@ -8646,6 +8672,14 @@ class definesvalidator{ public: static void Validate() {
#endif
+#ifdef VIAL // DO NOT MODIFY!
+ bsA = 1;
+ bsB = VIAL;
+ if(bsA!=bsB)
+ ssErrors << "Defined VIAL with value 1 from .dat file mismatches hardcoded c++ define value of " << VIAL << "!" << std::endl;
+#endif
+
+
#ifdef VITRELLOY // DO NOT MODIFY!
bsA = 4191;
bsB = VITRELLOY;
diff --git a/Main/Include/devcons.h b/Main/Include/devcons.h
new file mode 100644
index 000000000..7630da669
--- /dev/null
+++ b/Main/Include/devcons.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Iter Vehemens ad Necem (IVAN)
+ * Copyright (C) Timo Kiviluoto
+ * Released under the GNU General
+ * Public License
+ *
+ * See LICENSING which should be included
+ * along with this file for more details
+ *
+ */
+
+#ifndef MAIN_INCLUDE_DEVCONS_H_
+#define MAIN_INCLUDE_DEVCONS_H_
+
+#include
+
+#include "festring.h"
+
+#define DEVCMDMSG(fmt,x...) ADD_MESSAGE(" > " fmt,x);
+
+typedef void (*callcmd)(std::string);
+
+class devcons{
+ static callcmd Find(std::string strCmd);
+ static void Help(std::string strFilter);
+ static void runCommand(festring fsCmd);
+ public:
+ static void Init();
+ static void OpenCommandsConsole();
+ static void AddDevCmd(festring fsCmd, callcmd Call, festring fsHelp=festring(), bool bWizardModeOnly=false);
+ static void SetVar(std::string strParams);
+ static float GetVar(int iIndex,float fDefaultIf0);
+};
+
+#endif /* MAIN_INCLUDE_DEVCONS_H_ */
diff --git a/Main/Include/game.h b/Main/Include/game.h
index 622a9c279..8b255cfd7 100644
--- a/Main/Include/game.h
+++ b/Main/Include/game.h
@@ -288,12 +288,12 @@ class game
static v2 PositionQuestion(cfestring&, v2, positionhandler = 0, positionkeyhandler = 0, truth = true);
static void LookHandler(v2);
static int AskForKeyPress(cfestring&);
+ static bool IsQuestionMode();
static truth AnimationController();
static gamescript* GetGameScript() { return GameScript; }
static void InitScript();
static valuemap& GetGlobalValueMap() { return GlobalValueMap; }
static void InitGlobalValueMap();
- static void GenerateDefinesValidator(bool bValidade);
static void TextScreen(cfestring&, v2 = ZERO_V2, col16 = 0xFFFF, truth = true, truth = true, bitmapeditor = 0);
static void SetCursorPos(v2 What) { CursorPos = What; }
static truth DoZoom() { return Zoom; }
@@ -327,6 +327,10 @@ class game
static void UpdatePlayerAttributeAverage();
static void CallForAttention(v2, int);
static character* SearchCharacter(ulong);
+ static std::vector GetAllCharacters();
+ static characteridmap GetCharacterIDMapCopy();
+ static std::vector
- GetAllItems();
+ static itemidmap GetItemIDMapCopy();
static item* SearchItem(ulong);
static entity* SearchTrap(ulong);
static void AddCharacterID(character*, ulong);
@@ -366,6 +370,7 @@ class game
static truth MassacreListsEmpty();
static void PlayVictoryMusic();
static void PlayDefeatMusic();
+ static void SetMapNote(lsquare* lsqrN,festring What);
static bool ToggleDrawMapOverlay();
static void SetDrawMapOverlay(bool b);
static void RefreshDrawMapOverlay();
@@ -373,6 +378,8 @@ class game
static void DrawMapNotesOverlay(bitmap* =NULL);
static lsquare* GetHighlightedMapNoteLSquare();
static bool ToggleShowMapNotes();
+ static bool CheckAddAutoMapNote(square* =NULL);
+ static int CheckAutoPickup(square* sqr = NULL);
static int RotateMapNotes();
static char MapNoteToken();
@@ -481,7 +488,7 @@ class game
static int getDefaultItemsListWidth(){ return iListWidth; }
static void AddDebugDrawOverlayFunction(dbgdrawoverlay ddo){vDbgDrawOverlayFunctions.push_back(ddo);}
static int GetCurrentDungeonTurnsCount(){return iCurrentDungeonTurn;}
- static int GetSaveFileVersion();
+ static int GetSaveFileVersionHardcoded();
static void ValidateCommandKeys(char Key1,char Key2,char Key3);
private:
static void UpdateCameraCoordinate(int&, int, int, int);
diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h
index 8c96611f4..05efa70f1 100644
--- a/Main/Include/iconf.h
+++ b/Main/Include/iconf.h
@@ -29,18 +29,16 @@ class ivanconfig
static long GetShowItemsAtPlayerSquare(){ return ShowItemsAtPlayerSquare.Value; }
static long GetStartingWindowWidth() { return iStartingWindowWidth; }
static long GetStartingWindowHeight() { return iStartingWindowHeight; }
- static int GetBugWorkaroundDupPlayer() { return BugWorkaroundDupPlayer.Value; }
static long GetFrameSkip() { return FrameSkip.Value; }
static long GetGoOnStopMode() { return GoOnStopMode.Value; }
static long GetHoldPosMaxDist() { return HoldPosMaxDist.Value; }
- static truth IsSavegameSafely(){ return SavegameSafely.Value; }
static truth IsAllowImportOldSavegame(){ return AllowImportOldSavegame.Value; }
static long GetAltSilhouette() { return AltSilhouette.Value; }
static truth IsHideWeirdHitAnimationsThatLookLikeMiss(){return HideWeirdHitAnimationsThatLookLikeMiss.Value;}
- static truth IsGenerateDefinesValidator(){return GenerateDefinesValidator.Value;}
static int GetAltSilhouettePreventColorGlitch(){return AltSilhouettePreventColorGlitch.Value;}
static int GetShowMap(){return ShowMap.Value;}
static truth IsShowMapAtDetectMaterial() { return ShowMapAtDetectMaterial.Value; }
+ static truth IsTransparentMapLM() { return TransparentMapLM.Value; }
static truth IsWaitNeutralsMoveAway() { return WaitNeutralsMoveAway.Value; }
static truth IsEnhancedLights() { return EnhancedLights.Value; }
static int GetMemorizeEquipmentMode() { return MemorizeEquipmentMode.Value; }
@@ -52,6 +50,7 @@ class ivanconfig
static truth GetAutoDropLeftOvers() { return AutoDropLeftOvers.Value; }
static truth GetLookZoom() { return LookZoom.Value; }
static truth IsXBRZScale() { return XBRZScale.Value; }
+ static truth IsAutoPickupThrownItems() { return AutoPickupThrownItems.Value; }
static truth IsAltAdentureInfo() { return AltAdentureInfo.Value; }
static int GetXBRZSquaresAroundPlayer() { return XBRZSquaresAroundPlayer.Value; }
static int GetStartingDungeonGfxScale() { return iStartingDungeonGfxScale; }
@@ -143,7 +142,6 @@ class ivanconfig
static void ScalingQualityDisplayer(const cycleoption*, festring&);
static truth GraphicsScaleChangeInterface(cycleoption*);
static void GraphicsScaleChanger(cycleoption*, long);
- static void BugWorkaroundDupPlayerDisplayer(const cycleoption* O, festring& Entry);
static void FullScreenModeChanger(truthoption*, truth);
#endif
@@ -160,8 +158,6 @@ class ivanconfig
static void SilhouetteScaleChanger(cycleoption*, long);
static void SaveGameSortModeChanger(cycleoption* O, long What);
static void XBRZScaleChanger(truthoption*, truth);
- static void SavegameSafelyChanger(truthoption* O, truth What);
- static void GenerateDefinesValidatorChanger(truthoption* O, truth What);
static void ContrastHandler(long);
static void VolumeHandler(long);
static void BackGroundDrawer();
@@ -186,13 +182,12 @@ class ivanconfig
static numberoption FrameSkip;
static truthoption ShowFullDungeonName;
static truthoption AllowImportOldSavegame;
- static truthoption SavegameSafely;
static cycleoption ShowItemsAtPlayerSquare;
static truthoption HideWeirdHitAnimationsThatLookLikeMiss;
- static truthoption GenerateDefinesValidator;
static cycleoption AltSilhouettePreventColorGlitch;
static cycleoption ShowMap;
static truthoption ShowMapAtDetectMaterial;
+ static truthoption TransparentMapLM;
static truthoption WaitNeutralsMoveAway;
static truthoption EnhancedLights;
@@ -202,6 +197,7 @@ class ivanconfig
static truthoption AutoDropLeftOvers;
static truthoption LookZoom;
static truthoption XBRZScale;
+ static truthoption AutoPickupThrownItems;
static cycleoption SaveGameSortMode;
static cycleoption DistLimitMagicMushrooms;
@@ -241,7 +237,6 @@ class ivanconfig
static truthoption PlaySounds;
static truthoption ShowTurn;
- static cycleoption BugWorkaroundDupPlayer;
static truthoption AllowMouseOnFelist;
};
diff --git a/Main/Include/item.h b/Main/Include/item.h
index 6b2a14ab6..7f3a378f8 100644
--- a/Main/Include/item.h
+++ b/Main/Include/item.h
@@ -264,6 +264,9 @@ class item : public object
virtual truth IsExplosive() const { return false; }
virtual void SetLabel(cfestring& What);
virtual cfestring& GetLabel() const { return label; }
+ bool HasTag(char);
+ void SetTag(char);
+ void ClearTag(char tag);
virtual void AddName(festring&, int) const;
virtual void AddName(festring& a, int b, int c) const {object::AddName(a,b,c);} //required because of AddName(festring&,int)
virtual void Save(outputfile&) const;
diff --git a/Main/Include/ivandef.h b/Main/Include/ivandef.h
index 2a4042dfa..7f8971f2c 100644
--- a/Main/Include/ivandef.h
+++ b/Main/Include/ivandef.h
@@ -971,6 +971,7 @@ cv2 SILHOUETTE_SIZE(48, 64); // it is TILE_SIZE*3,TILE_SIZE*4 tho..
#define CAN_BE_DESTROYED 16
#define IS_VALUABLE 32
#define CAN_BE_MIRRORED 64
+#define CAN_BE_DETECTED 128
/* NameFlags */
#define USE_AN 1
diff --git a/Main/Include/proto.h b/Main/Include/proto.h
index 113140077..d965863f5 100644
--- a/Main/Include/proto.h
+++ b/Main/Include/proto.h
@@ -77,7 +77,8 @@ class protosystem
static character* CreateMonster(int = 1, int = 999999, int = 0);
static character* CreateMonster(cfestring&, int = 0, truth = true);
static item* CreateItem(cfestring&, truth = true);
- static material* CreateMaterial(cfestring&, long = 0, truth = true);
+ static material* CreateMaterial(cfestring&, long = 0, truth = true, truth = false);
+ static material* CreateMaterialForDetection(cfestring& What);
static void CreateEveryNormalEnemy(charactervector&);
#ifdef WIZARD
static void CreateEveryCharacter(charactervector&);
diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp
index 70cf01dea..d97127c77 100644
--- a/Main/Source/actions.cpp
+++ b/Main/Source/actions.cpp
@@ -338,19 +338,37 @@ void dig::Terminate(truth Finished)
void go::Save(outputfile& SaveFile) const
{
action::Save(SaveFile);
- SaveFile << Direction << WalkingInOpen;
+ SaveFile << Direction << WalkingInOpen << RouteGoOn;
}
void go::Load(inputfile& SaveFile)
{
action::Load(SaveFile);
- SaveFile >> Direction >> WalkingInOpen;
+ SaveFile >> Direction >> WalkingInOpen >> RouteGoOn;
+}
+
+void go::SetDirectionFromRoute()
+{
+ v2 next = RouteGoOn.back();
+ RouteGoOn.pop_back();
+ SetDirection(
+ game::GetDirectionForVector(
+ next-Actor->GetPos()));
}
void go::Handle()
{
+ bool bRouteMode = IsRouteMode();
+ if(bRouteMode)
+ SetDirectionFromRoute();
+
GetActor()->EditAP(GetActor()->GetStateAPGain(100)); // gum solution
- GetActor()->GoOn(this);
+ GetActor()->GoOn(this);
+
+ if(GetActor()->GetAction()) //may have been terminated by GoOn()
+ if(bRouteMode) //was route mode
+ if(RouteGoOn.size()==0) //currently is the last step
+ Terminate(false);
}
void study::Handle()
diff --git a/Main/Source/bugworkaround.cpp b/Main/Source/bugworkaround.cpp
index 6f6702b24..dcd78ec76 100644
--- a/Main/Source/bugworkaround.cpp
+++ b/Main/Source/bugworkaround.cpp
@@ -1,65 +1,159 @@
+/*
+ *
+ * Iter Vehemens ad Necem (IVAN)
+ * Copyright (C) Timo Kiviluoto
+ * Released under the GNU General
+ * Public License
+ *
+ * See LICENSING which should be included
+ * along with this file for more details
+ *
+ */
+
#include "char.h"
#include "bitmap.h"
#include "bugworkaround.h"
+#include "devcons.h"
#include "game.h"
#include "graphics.h"
#include "iconf.h"
+#include "message.h"
+#include "miscitem.h"
#include "rawbit.h"
#include "stack.h"
+#include "trap.h"
#include "whandler.h"
#include "dbgmsgproj.h"
-bool bugWorkaroundDupPlayer::Accepted=false;
-void bugWorkaroundDupPlayer::AlertConfirmFixMsg(const char* cMsg, bool bAbortIfNot=true){
- if(bugWorkaroundDupPlayer::Accepted)return;
+bool bAlertConfirmFixMsgDraw=false;
+bool bugfixdp::IsAlertConfirmFixMsgDraw()
+{
+ return bAlertConfirmFixMsgDraw;
+}
+
+festring fsLastAlertConfirmFixMsg;
+void bugfixdp::DrawAlertConfirmFix(bitmap* Buffer)
+{
+ if(!bAlertConfirmFixMsgDraw)return;
v2 v2Border(700,100);
v2 v2TL(RES.X/2-v2Border.X/2,RES.Y/2-v2Border.Y/2);
- DOUBLE_BUFFER->Fill(v2TL,v2Border,RED);
- graphics::DrawRectangleOutlineAround(DOUBLE_BUFFER, v2TL, v2Border, YELLOW, true);
- DOUBLE_BUFFER->Fill(v2TL,v2Border,RED);
+ Buffer->Fill(v2TL,v2Border,DARK_GRAY);
+ graphics::DrawRectangleOutlineAround(Buffer, v2TL, v2Border, YELLOW, true);
v2TL+=v2(16,16);
- FONT->Printf(DOUBLE_BUFFER, v2(v2TL.X,v2TL.Y ), YELLOW, "%s", cMsg);
- FONT->Printf(DOUBLE_BUFFER, v2(v2TL.X,v2TL.Y+16), BLUE, "%s", "Please backup your savegame files.");
- FONT->Printf(DOUBLE_BUFFER, v2(v2TL.X,v2TL.Y+32), WHITE, "%s", "Try to fix it? (y/...)");
- graphics::BlitDBToScreen(); //as the final blit may be from StretchedBuffer
+ int iLH=16;
+
+ FONT->Printf(Buffer, v2(v2TL.X,v2TL.Y), YELLOW, "%s", fsLastAlertConfirmFixMsg.CStr());
+
+ cchar* c1="Please backup your savegame files.";
+ v2TL.Y+=iLH;
+ FONT->Printf(Buffer, v2(v2TL.X,v2TL.Y), BLUE, "%s", c1);
+
+ cchar* c2="Confirm? (y/...)";
+ v2TL.Y+=iLH;
+ FONT->Printf(Buffer, v2(v2TL.X,v2TL.Y), WHITE, "%s", c2);
+}
+
+bool bugfixdp::AlertConfirmFixMsg(const char* cMsg){
+ fsLastAlertConfirmFixMsg.Empty();
+ fsLastAlertConfirmFixMsg<<"PROBLEM! "< vTrapAll;
+ for(int iY=0;iYGetYSize();iY++){//if(bChangeItemID)break;
+ for(int iX=0;iXGetXSize();iX++){//if(bChangeItemID)break;
+ lsquare* lsqr = game::GetCurrentLevel()->GetLSquare({iX,iY});
+ lsqr->FillTrapVector(vTrapAll);
+ }
+ }
+
+ std::vector vTrapErase;
+ for(int i=0;iGetTrapID()==tj->GetTrapID()){
+ vTrapErase.push_back(tj);
+ }
+ }
+ }
+
+ for(int i=0;i(t)!=NULL)
+ ((beartrap*)t)->RemoveFromSlot();
+ t->GetLSquareUnder()->RemoveTrap(t);
+ iTot++;
+ //TODO anything can be done for gas and mines?
+ }
+
+ return iTot;
}
-void bugWorkaroundDupPlayer::CharEquipmentsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+int bugfixdp::CharEquipmentsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+ int iFixedCount=0;
for(int i=0;iGetEquipments();i++)
- bugWorkaroundDupPlayer::ItemWork(CharAsked,CharAsked->GetEquipment(i),bFix,"CharFix:Equipped",pvItem,bSendToHell);
+ if(bugfixdp::ItemWork(CharAsked,CharAsked->GetEquipment(i),bFix,"CharFix:Equipped",pvItem,bSendToHell))
+ iFixedCount++;
+ return iFixedCount;
}
-void bugWorkaroundDupPlayer::CharInventoryWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+int bugfixdp::CharInventoryWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+ int iFixedCount=0;
stack* stk=CharAsked->GetStack(); //inventory
for(int i=0;iGetItems();i++)
- bugWorkaroundDupPlayer::ItemWork(CharAsked,stk->GetItem(i),bFix,"CharFix:Inventory",pvItem,bSendToHell);
+ if(bugfixdp::ItemWork(CharAsked,stk->GetItem(i),bFix,"CharFix:Inventory",pvItem,bSendToHell))
+ iFixedCount++;
+ return iFixedCount;
}
-void bugWorkaroundDupPlayer::CharBodypartsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+int bugfixdp::CharBodypartsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+ int iFixedCount=0;
for(int i=0;iGetBodyParts();i++)
- bugWorkaroundDupPlayer::ItemWork(CharAsked,CharAsked->GetBodyPart(i),bFix,"CharFix:BodyPart",pvItem,bSendToHell);
+ if(bugfixdp::ItemWork(CharAsked,CharAsked->GetBodyPart(i),bFix,"CharFix:BodyPart",pvItem,bSendToHell))
+ iFixedCount++;
+ return iFixedCount;
}
-void bugWorkaroundDupPlayer::CharAllItemsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
- bugWorkaroundDupPlayer::CharEquipmentsWork(CharAsked, bFix, bSendToHell, pvItem);
- bugWorkaroundDupPlayer::CharInventoryWork (CharAsked, bFix, bSendToHell, pvItem);
- bugWorkaroundDupPlayer::CharBodypartsWork (CharAsked, bFix, bSendToHell, pvItem);
+int bugfixdp::CharAllItemsWork(character* CharAsked, bool bFix, bool bSendToHell, std::vector
- * pvItem){
+ int iFixedCount=0;
+ iFixedCount+=bugfixdp::CharEquipmentsWork(CharAsked, bFix, bSendToHell, pvItem);
+ iFixedCount+=bugfixdp::CharInventoryWork (CharAsked, bFix, bSendToHell, pvItem);
+ iFixedCount+=bugfixdp::CharBodypartsWork (CharAsked, bFix, bSendToHell, pvItem);
+ return iFixedCount;
}
-void bugWorkaroundDupPlayer::CharAllItemsInfo(character* CharAsked){
- bugWorkaroundDupPlayer::CharAllItemsWork(CharAsked, false, false, NULL);
+void bugfixdp::CharAllItemsInfo(character* CharAsked){
+ bugfixdp::CharAllItemsWork(CharAsked, false, false, NULL);
}
-void bugWorkaroundDupPlayer::CharAllItemsCollect(character* CharAsked,std::vector
- * pvItem){
- bugWorkaroundDupPlayer::CharAllItemsWork(CharAsked, false, false, pvItem);
+void bugfixdp::CharAllItemsCollect(character* CharAsked,std::vector
- * pvItem){
+ bugfixdp::CharAllItemsWork(CharAsked, false, false, pvItem);
}
-void bugWorkaroundDupPlayer::CollectAllCharactersOnLevel(std::vector* pvCharsOnLevel)
+void bugfixdp::CollectAllCharactersOnLevel(std::vector* pvCharsOnLevel)
{
std::vector pvAllCharAndOrItemsInLevel;
ScanLevelForCharactersAndItemsWork(NULL,true,false,&pvAllCharAndOrItemsInLevel);
@@ -81,7 +175,7 @@ void bugWorkaroundDupPlayer::CollectAllCharactersOnLevel(std::vector
* it may be filled with duplicated items references (pointers) and IDs,
* that's the idea, to look for each for DUPs (or multiples) later.
*/
-void bugWorkaroundDupPlayer::CollectAllItemsOnLevel(std::vector
- * pvAllItemsOnLevel)
+void bugfixdp::CollectAllItemsOnLevel(std::vector
- * pvAllItemsOnLevel)
{
std::vector pvAllCharAndOrItemsInLevel;
ScanLevelForCharactersAndItemsWork(NULL,true,false,&pvAllCharAndOrItemsInLevel);
@@ -90,17 +184,17 @@ void bugWorkaroundDupPlayer::CollectAllItemsOnLevel(std::vector
- * pvAllIte
pvAllItemsOnLevel->push_back(pvAllCharAndOrItemsInLevel[i].it);
}
-void bugWorkaroundDupPlayer::CollectAllCharactersAndItemsOnLevel(std::vector* pvAllCharAndOrItemsInLevel)
+void bugfixdp::CollectAllCharactersAndItemsOnLevel(std::vector* pvAllCharAndOrItemsInLevel)
{
ScanLevelForCharactersAndItemsWork(NULL,false,false,pvAllCharAndOrItemsInLevel);
}
-bool bugWorkaroundDupPlayer::FindDupItemOnLevel(item* itWork, bool bIgnoreBodyParts, bool bAbortOnMultiples)
+bool bugfixdp::FindDupItemOnLevel(item* itWork, bool bIgnoreBodyParts, bool bAbortOnMultiples)
{
return ScanLevelForCharactersAndItemsWork(itWork,bIgnoreBodyParts,bAbortOnMultiples,NULL);
}
-bool bugWorkaroundDupPlayer::ScanLevelForCharactersAndItemsWork(
+bool bugfixdp::ScanLevelForCharactersAndItemsWork(
item* itWork,
bool bIgnoreBodyParts,
bool bAbortOnMultiples, // more than dups
@@ -111,27 +205,32 @@ bool bugWorkaroundDupPlayer::ScanLevelForCharactersAndItemsWork(
int iPointerMatchCount=0;
for(int iY=0;iYGetYSize();iY++){//if(bChangeItemID)break;
for(int iX=0;iXGetXSize();iX++){//if(bChangeItemID)break;
- lsquare* lsqr = game::GetCurrentLevel()->GetLSquare({iX,iY});
+ square* sqr = game::GetCurrentArea()->GetSquare({iX,iY});
std::vector
- vSqrItems;
- stack* stk = lsqr->GetStack();
- for(int i=0;iGetItems();i++){//if(bChangeItemID)break;
- vSqrItems.push_back(stk->GetItem(i));
+ level* lvl = game::GetCurrentLevel();
+ if(lvl!=NULL){
+ lsquare* lsqr = game::GetCurrentLevel()->GetLSquare({iX,iY});
- if(pvAllCharAndOrItemsInLevel!=NULL){
- bugWorkaroundDupPlayerCharItem ci;
- ci.Char=NULL;
- ci.it=stk->GetItem(i);
- pvAllCharAndOrItemsInLevel->push_back(ci);
+ stack* stk = lsqr->GetStack();
+ for(int i=0;iGetItems();i++){//if(bChangeItemID)break;
+ vSqrItems.push_back(stk->GetItem(i));
+
+ if(pvAllCharAndOrItemsInLevel!=NULL){
+ bugWorkaroundDupPlayerCharItem ci;
+ ci.Char=NULL;
+ ci.it=stk->GetItem(i);
+ pvAllCharAndOrItemsInLevel->push_back(ci);
+ }
}
}
- character* SqrChar = lsqr->GetCharacter();
+ character* SqrChar = sqr->GetCharacter();
//if(SqrChar!=NULL && SqrChar!=Char){
if(SqrChar!=NULL){ //w/o skipping the Char, this will check for dups inside self Char inventory!
std::vector
- vCharItems;
- bugWorkaroundDupPlayer::CharAllItemsCollect(SqrChar,&vCharItems);
+ bugfixdp::CharAllItemsCollect(SqrChar,&vCharItems);
for(int i=0;iGetPos()),vSqrItems[i],itWork,itWork->GetID(),(SqrChar==NULL?0:SqrChar),(SqrChar==NULL?0:SqrChar->GetID()))
+ #define DBGSQRITEM(msg) DBG7(msg,DBGAV2(sqr->GetPos()),vSqrItems[i],itWork,itWork->GetID(),(SqrChar==NULL?0:SqrChar),(SqrChar==NULL?0:SqrChar->GetID()))
#else
#define DBGSQRITEM(msg)
#endif
@@ -185,7 +284,7 @@ bool bugWorkaroundDupPlayer::ScanLevelForCharactersAndItemsWork(
return iDupIDCount>0;
}
-void bugWorkaroundDupPlayer::ItemWork(character* Char, item* itWork, bool bFix, const char* cInfo, std::vector
- * pvItem, bool bSendToHell){
+bool bugfixdp::ItemWork(character* Char, item* itWork, bool bFix, const char* cInfo, std::vector
- * pvItem, bool bSendToHell){
if(itWork!=NULL){
if(pvItem!=NULL)pvItem->push_back(itWork);
@@ -204,7 +303,7 @@ void bugWorkaroundDupPlayer::ItemWork(character* Char, item* itWork, bool bFix,
}
if(!bChangeItemID){
- bChangeItemID = bugWorkaroundDupPlayer::FindDupItemOnLevel(itWork,false,true);
+ bChangeItemID = bugfixdp::FindDupItemOnLevel(itWork,false,true);
}
if(bChangeItemID){
@@ -221,138 +320,334 @@ void bugWorkaroundDupPlayer::ItemWork(character* Char, item* itWork, bool bFix,
if(bSendToHell){
itWork->SendToHell();
- DBG3("SentToHell:ItemsID",DBGI(itWork->GetID()),itWork);
+ DBG3("CharFix:SentToHell:ItemsID",DBGI(itWork->GetID()),itWork);
}
+
+ return bMakeItConsistent;
}else{
DBG6(Char,Char->GetID(),cInfo,"CharFix:ItemID",DBGI(itWork->GetID()),itWork); //some helpful info for comparison and understanding
}
}
+
+ return false;
}
-character* bugWorkaroundDupPlayer::BugWorkaroundDupPlayer(character* CharAsked, v2 v2AskedPos){ DBG2(CharAsked,DBGAV2(v2AskedPos));
- bool bAllowPlayerBugFix = ivanconfig::GetBugWorkaroundDupPlayer()!=0;
- bool bAllowJustMissingPlayerBugFix = ivanconfig::GetBugWorkaroundDupPlayer()==1;
- bool bNewPlayerInstanceShallWin = ivanconfig::GetBugWorkaroundDupPlayer()==3; //==2 is old player instance
+void bugfixdp::ValidateFullLevel()
+{
+ DEVCMDMSG("%s","validate full level against dup stuff");
- if(CharAsked!=NULL && !CharAsked->IsPlayer()){
- DBGCHAR(CharAsked,"CharFix:CharAsked:IsNotThePlayer");
- CharAsked=NULL; //deny invalid character
+ // validate full level against other possible dup items
+ std::vector
- vAllItemsOnLevel;
+ bugfixdp::CollectAllItemsOnLevel(&vAllItemsOnLevel);
+ for(int i=0;iGetID();
+ DupPlayer->_BugWorkaround_PlayerDup(game::CreateNewCharacterID(DupPlayer));DBGLN; // make it consistent as removing it is crashing (also empties inv)
+ for(int i=0;iGetEquipments();i++){ // clear equipments too
+ if(DupPlayer->CanUseEquipment(i)){
+ item* it = DupPlayer->GetEquipment(i);
+ if(it!=NULL){
+ DupPlayer->SetEquipment(i,NULL); //this leaves untracked objects in memory. TODO really untracked?
+// if(SearchItem(it->GetID())!=NULL){
+// RemoveItemID(it->GetID()); //TODO could such item pointer or ID be still referenced somewhere?
+// }
+ DBG5(DupPlayer,"CharFix:EquipmentRemoved",i,DBGI(it->GetID()),it);
+ }
}
}
+ DupPlayer->RemoveFlags(C_PLAYER);DBGLN;
+ DupPlayer->SetTeam(game::GetTeam(MONSTER_TEAM));DBGLN;
+ DupPlayer->SetAssignedName("_DupPlayerBug_");DBGLN; //non immersive naming, shall not be, this bug shall be properly fixed one day.
- bugWorkaroundDupPlayer::Accepted=false; //init to ask again if needed, so the user knows what is happening
+ //TODO add other effects to auto-kill it, poison? spill sulf acid? must gas? polymorph into y.bunny?
+ DupPlayer->LoseConsciousness(100000,false); //may not fall asleep tho
+ DupPlayer->SetNP(1); //to die soon at least
- character* CharPlayer = game::SearchCharacter(1);
- if(CharPlayer==NULL){
- std::vector vCharsOnLevel;
- bugWorkaroundDupPlayer::CollectAllCharactersOnLevel(&vCharsOnLevel);
- for(int i=0;iGetPos()));
+ iFixedCount=CharBodypartsWork(DupPlayer,true,false);DBGLN; //bodyparts sent to hell would crash!!! TODO only torso?
+ DEVCMDMSG("fixed bodyparts %d",iFixedCount);
+ //BEWARE!!! this leads to crash: DupPlayer->Remove();
- if(vCharsOnLevel[i]->GetID()==1){
- DBGCHAR(vCharsOnLevel[i],"CharWithID=1");
- DBG2("CharFix:CharID1FoundAt",DBGAV2(vCharsOnLevel[i]->GetPos()));
- }
+ DEVCMDMSG("fixed dup player '%s' id=%d/%d 0x%X",DupPlayer->GetName(DEFINITE).CStr(),idOld,DupPlayer->GetID(),DupPlayer);
- if(vCharsOnLevel[i]->IsPlayer()){
- CharPlayer=vCharsOnLevel[i];
- DBG2("CharFix:PlayerFoundAt",DBGAV2(CharPlayer->GetPos()));
- break;
- }
- }
+ DBGCHAR(DupPlayer,"CharFix:CharToBeLost");
+}
+
+void bugfixdp::init()
+{
+ devcons::AddDevCmd("FixDupPlayer",bugfixdp::DevConsCmd,
+ "BugFix DUP player (experimental/slow). Ps.: If the player went missing, it will kickin automatically on loading.");
+// "[full] BugFix DUP player (experimental/slow). The dup player may just be the polymorphed, otherwise the full optional mode will try harder to fix it. Ps.: If the player went missing, it will kickin automatically on loading.");
+}
- if(CharPlayer==NULL)ABORT("Player cant be found anywhere"); //OMG... may be this is pointless, better just fix to prevent such bugs from ever happening...
+character* bugfixdp::ValidatePlayerAt(square* sqr)
+{
+ v2 Pos = sqr->GetPos();
+ character* CharAtPos = sqr->GetCharacter();
+
+ std::vector vPF = FindByPlayerFlag();
+
+ //very consistent!
+ if(CharAtPos!=NULL && CharAtPos->IsPlayer() && vPF.size()==1)
+ if(CharAtPos->GetID()==1 || (CharAtPos->GetPolymorphBackup() && CharAtPos->GetPolymorphBackup()->GetID()==1))
+ return CharAtPos;
+
+ //////////////////// there are problems if reaching here ////////////////////
+
+ /*
+ * TODO
+ * there is some double-checks/validationRepetition below...
+ * the flow below could be less confusing if possible...
+ */
+
+ character* CharID1 = FindByPlayerID1(Pos,true); //player is already fixed here, before the question below at (*1)
+
+ if(vPF.size()==0 && CharID1==NULL){ //TODO let user chose one NPC to become the player? sounds too messy tho...
+ // this can't be fixed automatically
+ ABORT("Player can't be found anywhere! Try to restore a backup if available.");
}
- DBGCHAR(CharPlayer,"CharFix:CharPlayer");
- if(bAllowJustMissingPlayerBugFix)return CharPlayer;
+ if(vPF.size()>1){ // FIRST CHECK/FIX!
+ festring fsMsg;
+ fsMsg << "Multiple player instances found (x" << vPF.size() << "), try to fix this?";
+ if(AlertConfirmFixMsg(fsMsg.CStr()))
+ return BugWorkaroundDupPlayer();
+ /**
+ * a problem like moving two characters in alternating turns would happen,
+ * also unable to move between dungeons w/o crashing
+ */
+ ABORT("The game would become inconsistent (prone to crash) w/o fixing this problem '%s'",fsMsg.CStr());
+ }
- character* CharWins = CharAsked;
- character* CharPlayerOld = NULL;
- bool bLevelItemsCollected=false;
- if(CharPlayer!=CharAsked){
- bugWorkaroundDupPlayer::AlertConfirmFixMsg(
- bNewPlayerInstanceShallWin ?
- "Duplicated player found. Fix new player instance (experimental)?" :
- "Duplicated player found. Restore old player instance (experimental)?" );
+ if(vPF.size()==1){
+ if(vPF[0]==CharID1){
+ festring fsMsg;
+ fsMsg << "There was some problem but the Player character was found and moved to requested position " << Pos.X<<","<IsPlayer()){
+ festring fsMsg;
+ fsMsg << "Requested location " << Pos.X<<","<GetID()); //actually id 1
- // the new char is not valid and not consistent at char's list yet, so...
- game::AddCharacterID(CharAsked,CharAsked->GetID()); //this will "update" the player ID (actually id 1) to the new character making it consistent on the list
- }
+bool IsPlayerPB(character* C1, character* C2){
+ if(C1->GetPolymorphBackup()==C2 && C2->IsPlayer())return true;
+ if(C2->GetPolymorphBackup()==C1 && C1->IsPlayer())return true;
+ return false;
+}
-// // prepare to check for dup item's ids
-// bugWorkaroundDupPlayer::GatherAllItemInLevel();DBGLN;
-// bLevelItemsCollected=true;
+std::vector bugfixdp::FindByPlayerFlag()
+{
+ return FindCharactersOnLevel(true);
+}
- //TODO transfer old player items (pointers/objects/instances) to new player instance if they have the same ID?
+/**
+ * tests for the CL_PLAYER flag
+ * @param bOnlyPlayers
+ * @return
+ */
+std::vector bugfixdp::FindCharactersOnLevel(bool bOnlyPlayerFlag)
+{
+ std::vector v;
+ for(int iY=0;iYGetYSize();iY++){for(int iX=0;iXGetXSize();iX++){
+ square* sqr = game::GetCurrentArea()->GetSquare({iX,iY});
+ character* C=sqr->GetCharacter();
+ if(C==NULL)continue;
+ if(!bOnlyPlayerFlag || C->IsPlayer())
+ v.push_back(C);
+ }}
+ return v;
+}
- if(bNewPlayerInstanceShallWin){
- // old player's items are consistent (on the list), they will get a new ID and be sent to hell
- // new player's items have the same ID of old player's ones, their pointers will become consistent later...
- bugWorkaroundDupPlayer::CharEquipmentsWork(CharPlayerOld,true,true);DBGLN;
- bugWorkaroundDupPlayer::CharInventoryWork(CharPlayerOld,true,true);DBGLN;
- }
- CharToBeLost->_BugWorkaround_PlayerDup(game::CreateNewCharacterID(CharToBeLost));DBGLN; // make it consistent as removing it is crashing (also empties inv)
- for(int i=0;iGetEquipments();i++){ // clear equipments too
- if(CharToBeLost->CanUseEquipment(i)){
- item* it = CharToBeLost->GetEquipment(i);
- if(it!=NULL){
- CharToBeLost->SetEquipment(i,NULL); //this leaves untracked objects in memory. TODO really untracked?
-// if(SearchItem(it->GetID())!=NULL){
-// RemoveItemID(it->GetID()); //TODO could such item pointer or ID be still referenced somewhere?
-// }
- DBG5(CharToBeLost,"CharFix:EquipmentRemoved",i,DBGI(it->GetID()),it);
- }
- }
+bool bBufFixDPMode=false;
+bool bugfixdp::IsFixing(){return bBufFixDPMode;}
+
+void bugfixdp::DevConsCmd(std::string strCmdParams)
+{
+ BugWorkaroundDupPlayer();
+ bBufFixDPMode=true;
+}
+
+character* bugfixdp::FindByPlayerID1(v2 ReqPosL,bool bAndFixIt)
+{DBGSV2(ReqPosL);
+ character* CharID1 = game::SearchCharacter(1); //this can ONLY return one char with ID=1 EVER, so there wont be a DUP char with ID=1 on the characters' map
+ if(CharID1==NULL)
+ return NULL;
+ DBGSV2(CharID1->GetPos());
+
+ character* PBID1=NULL;
+ character* PPolymL = NULL;
+ std::vector vCL = FindCharactersOnLevel();
+ for(int j=0;jGetPolymorphBackup();
+// if(PB!=NULL && PB==CharID1){
+ if(PBtmp!=NULL && PBtmp->GetID()==1){
+ PBID1 = PBtmp;
+ DEVCMDMSG("polymorphed has backup with id=1 ref 0x%X",PBID1);
+ PPolymL=vCL[j];
+ break;
}
- CharToBeLost->RemoveFlags(C_PLAYER);DBGLN;
- CharToBeLost->SetTeam(game::GetTeam(MONSTER_TEAM));DBGLN;
- CharToBeLost->SetAssignedName("_DupPlayerBug_");DBGLN; //non immersive naming, shall not be, this bug shall be properly fixed one day.
+ }
-// // prepare to check for dup item's ids
-// bugWorkaroundDupPlayer::GatherAllItemInLevel();DBGLN;
-// bLevelItemsCollected=true;
+// if(bAndFixIt && PBID1!=NULL && PBID1!=CharID1){
+// PPolymL->SetPolymorphBackup(CharID1);
+// DEVCMDMSG("fixing PB ref to correct id=1 ref 0x%X",CharID1);
+//
+// characteridmap Cmap = game::GetCharacterIDMapCopy();
+//// bool b=false;
+// for(characteridmap::iterator itr=Cmap.begin();itr!=Cmap.end();itr++){
+// if(itr->second==PBID1){
+//// b=true;
+// DEVCMDMSG("ref 0x%X actually has id=%d ",PBID1,itr->first); //TODO remove? send to hell? needed?
+//// if(itr->first!=1)
+//// game::RemoveCharacterID(itr->first);
+// break;
+// }
+// }
+//// if(b){
+//// DEVCMDMSG("inconsistent PBID1 ref 0x%X",PBID1);
+//// if(PBID1->GetSquareUnder()!=NULL){
+//// //THIS CRASHES!!! pointing to invalid SquareUnder reference ----> PBID1->Remove();
+//// }
+//// //there is no point in sending to hell as is not on consistent map ---> PBID1->SendToHell();
+//// }
+// }
+
+ character* CharPlayerOk = NULL;
+ if(bAndFixIt && PPolymL!=NULL){
+// DEVCMDMSG("killing polymorphed id=%d",PPolymL->GetID());
+ DEVCMDMSG("vanishing polymorphed id=%d",PPolymL->GetID());
+ PPolymL->SetPolymorphBackup(NULL);
+ PPolymL->RemoveTraps();
+ PPolymL->Remove();
+// PPolymL->Disable();
+ PPolymL->SendToHell();
+// PPolymL->Die(NULL,CONST_S(""), FORBID_REINCARNATION|DISALLOW_MSG|DISALLOW_CORPSE);
+
+// DEVCMDMSG("polymorphed found id=%d",PPolymL->GetID());
+// CharPlayerOk = PPolymL;
+// if(bAndFixIt && CharID1->GetSquareUnder()!=NULL){
+//// DEVCMDMSG("keeping the polymorphed id=%d and removing from level the ID1",PPolymL->GetID());
+// DEVCMDMSG("re-applying the polymorph to id=%d",PPolymL->GetID());
+// PPolymL->Remove();
+// CharID1->Polymorph(PPolymL,PPolymL->GetTemporaryStateCounter(POLYMORPHED));
+//// // fix based on character::Polymorph()
+//// CharID1->RemoveFlags(C_PLAYER);
+//// CharID1->Remove(); //the polymorphed will go back to it later
+// }
+ }
+// }else{
+ CharPlayerOk = CharID1;
+ DEVCMDMSG("%s","ID1 will be ok now");
+ if(bAndFixIt && CharID1->GetSquareUnder()==NULL){
+ DEVCMDMSG("placing the character ID1 at %d,%d",ReqPosL.X,ReqPosL.Y);
+ CharID1->PutToOrNear(ReqPosL); //place he where expected
+ }
+// }
- bugWorkaroundDupPlayer::CharBodypartsWork(CharToBeLost,true,false);DBGLN;
- // this leads to crash //CharAsked->Remove();
+ if(bAndFixIt)
+ if(!CharPlayerOk->IsPlayer() || PLAYER!=CharPlayerOk){
+ DEVCMDMSG("restoring player reference to id=%d",CharPlayerOk->GetID());
+ game::SetPlayer(CharPlayerOk);
+ }
- DBGCHAR(CharToBeLost,"CharFix:CharToBeLost");
+ return CharPlayerOk;
+}
- CharWins = bNewPlayerInstanceShallWin ? CharAsked : CharPlayerOld;
+character* bugfixdp::BugWorkaroundDupPlayer(){
+ character* CharID1 = game::SearchCharacter(1); //this can ONLY return one char with ID=1 EVER, so there wont be a DUP char with ID=1 on the characters' map
+ if(CharID1==NULL)
+ ABORT("Can't find the valid player.");
+
+ std::vector vCL = FindCharactersOnLevel(); DBG1(vCL.size());
+ v2 ReqPosL; //TODO default to the entrance (not the exit). Or random?
+ for(int j=0;jIsPlayer() && vCL[j]->GetSquareUnder()!=NULL){
+ ReqPosL=vCL[j]->GetPos();
+ break;
+ }
}
- // now, grants the valid player has no item issues compared to other items on the level/chars TODO this may create duplicated items?
-// if(!bLevelItemsCollected){bugWorkaroundDupPlayer::GatherAllItemInLevel();DBGLN;}
- // new player's items will be made consistent if required (added to the list)
- bugWorkaroundDupPlayer::CharEquipmentsWork(CharWins,true,false);DBGLN;
- bugWorkaroundDupPlayer::CharInventoryWork(CharWins,true,false);DBGLN;
+ character* CharPlayerOk = FindByPlayerID1(ReqPosL,true);
+ if(CharPlayerOk==NULL)
+ ABORT("Unable to fix the valid player.");
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////// below is in case the dup player is not consistent on the characters map /////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ for(int j=0;jGetID(),vCL[j]->IsPlayer(),vCL[j]->GetPolymorphBackup());
+ if(vCL[j]==CharPlayerOk)
+ continue;
+ if(vCL[j]->GetID()==1)
+ continue;
+ if(vCL[j]->IsPlayer() || vCL[j]->GetPolymorphBackup()==CharID1)
+ DupPlayerFix(vCL[j]);
+ }
- // validate full level against other possible dup items
- std::vector
- vAllItemsOnLevel;
- bugWorkaroundDupPlayer::CollectAllItemsOnLevel(&vAllItemsOnLevel);
- for(int i=0;ifirst;if(Cid==0)continue;
+ character* C = itr->second;if(C==NULL)continue;
+ bool bFound=false;
+ for(int j=0;jGetPolymorphBackup()==C){
+ bFound=true;
+ break;
+ }
+ if(!bFound){
+// game::RemoveCharacterID(Cid); //causes weird crashes elsewhere
+// DEVCMDMSG("removed inconsistent character id '%d'",Cid);
+ DEVCMDMSG("possibly inconsistent character id '%d'",Cid);
}
}
- DBGCHAR(CharWins,"CharFix:CharWins");
- return CharWins;
+ // last thing is grant player's stuff is consistent
+ int iFixedCount=0;
+ iFixedCount=CharEquipmentsWork(CharPlayerOk,true,false);DBGLN;
+ DEVCMDMSG("fixed player '%s' equipments %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount);
+ iFixedCount=CharInventoryWork (CharPlayerOk,true,false);DBGLN;
+ DEVCMDMSG("fixed player '%s' inventory %d",CharPlayerOk->GetName(DEFINITE).CStr(),iFixedCount);
+ iFixedCount=TrapsWork();
+ DEVCMDMSG("fixed traps %d",iFixedCount);
+
+ // just a final validation, may abort on failure
+ ValidateFullLevel();
+
+ return CharPlayerOk;
}
diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp
index a5b1db0aa..e3d0c853a 100644
--- a/Main/Source/char.cpp
+++ b/Main/Source/char.cpp
@@ -2157,7 +2157,7 @@ void character::Save(outputfile& SaveFile) const
SaveFile << BodyPartSlot[c] << OriginalBodyPartID[c];
SaveLinkedList(SaveFile, TrapData);
- SaveFile << Action;
+ SaveFile << Action; DBG1(Action);
for(c = 0; c < STATES; ++c)
SaveFile << TemporaryStateCounter[c];
@@ -3406,6 +3406,10 @@ void character::GetPlayerCommand()
{
bool bWaitNeutralMove=false;
HasActed = TryMove(ApplyStateModification(game::GetMoveVector(c)), true, game::PlayerIsRunning(), &bWaitNeutralMove);
+ if(HasActed){
+ game::CheckAddAutoMapNote();
+ game::CheckAutoPickup();
+ }
if(!HasActed && bWaitNeutralMove){
//cant access.. HasActed = commandsystem::NOP(this);
Key = '.'; //TODO request NOP()'s key instead of this '.' hardcoded here. how?
@@ -4217,73 +4221,14 @@ truth character::MoveRandomlyInRoom()
return false;
}
-void character::GoOn(go* Go, truth FirstStep)
+truth character::IsAboveUsefulItem()
{
- v2 MoveVector = ApplyStateModification(game::GetMoveVector(Go->GetDirection()));
- lsquare* MoveToSquare[MAX_SQUARES_UNDER];
- int Squares = CalculateNewSquaresUnder(MoveToSquare, GetPos() + MoveVector);
-
- if(!Squares || !CanMoveOn(MoveToSquare[0]))
- {
- Go->Terminate(false);
- return;
- }
-
- uint OldRoomIndex = GetLSquareUnder()->GetRoomIndex();
- uint CurrentRoomIndex = MoveToSquare[0]->GetRoomIndex();
-
- if((OldRoomIndex && (CurrentRoomIndex != OldRoomIndex)) && !FirstStep)
- {
- Go->Terminate(false);
- return;
- }
-
- for(int c = 0; c < Squares; ++c)
- if((MoveToSquare[c]->GetCharacter() && GetTeam() != MoveToSquare[c]->GetCharacter()->GetTeam())
- || MoveToSquare[c]->IsDangerous(this))
- {
- Go->Terminate(false);
- return;
- }
-
- int OKDirectionsCounter = 0;
-
- for(int d = 0; d < GetNeighbourSquares(); ++d)
- {
- lsquare* Square = GetNeighbourLSquare(d);
-
- if(Square && CanMoveOn(Square))
- ++OKDirectionsCounter;
- }
-
- if(!Go->IsWalkingInOpen())
- {
- if(OKDirectionsCounter > 2)
- {
- Go->Terminate(false);
- return;
- }
- }
- else
- if(OKDirectionsCounter <= 2)
- Go->SetIsWalkingInOpen(false);
-
- square* BeginSquare = GetSquareUnder();
-
- if(!TryMove(MoveVector, true, game::PlayerIsRunning())
- || BeginSquare == GetSquareUnder()
- || (CurrentRoomIndex && (OldRoomIndex != CurrentRoomIndex)))
- {
- Go->Terminate(false);
- return;
- }
-
if(GetStackUnder()->GetVisibleItems(this))
{
bool bUseless=false,bTooCheap=false,bEncumbering=false;
switch(ivanconfig::GetGoOnStopMode()){
- case 0: Go->Terminate(false); return;
+ case 0: return true;
case 1:bUseless=true;break;
case 2:bTooCheap=true;break;
case 3:bEncumbering=true;break;
@@ -4307,18 +4252,13 @@ void character::GoOn(go* Go, truth FirstStep)
vit[i]->IsAppliable(this) ||
vit[i]->IsZappable(this) ||
- // bad! vit[i]->IsConsumable() ||
+ // bad! keep as info! vit[i]->IsConsumable() ||
vit[i]->IsEatable(this) ||
vit[i]->IsDrinkable(this) ||
- // bad! vit[i]->AllowEquip() ||
+ // bad! keep as info! vit[i]->AllowEquip() ||
vit[i]->IsWeapon(this) ||
vit[i]->IsArmor(this) || //all armor slots
-// vit[i]->IsBodyArmor(this) ||
-// vit[i]->IsHelmet(this) ||
-// vit[i]->IsGauntlet(this) ||
-// vit[i]->IsBoot(this) ||
-// vit[i]->IsBelt(this) ||
vit[i]->IsAmulet(this) ||
vit[i]->IsRing(this) ||
@@ -4333,10 +4273,82 @@ void character::GoOn(go* Go, truth FirstStep)
(vit[i]->GetTruePrice()/(vit[i]->GetWeight()/1000.0)) > (iMaxValueless*2)
)
){
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void character::GoOn(go* Go, truth FirstStep)
+{
+ v2 MoveVector = ApplyStateModification(game::GetMoveVector(Go->GetDirection()));
+ lsquare* MoveToSquare[MAX_SQUARES_UNDER];
+ int Squares = CalculateNewSquaresUnder(MoveToSquare, GetPos() + MoveVector);
+
+ if(!Squares || !CanMoveOn(MoveToSquare[0]))
+ {
+ Go->Terminate(false);
+ return;
+ }
+
+ uint OldRoomIndex = GetLSquareUnder()->GetRoomIndex();
+ uint CurrentRoomIndex = MoveToSquare[0]->GetRoomIndex();
+
+ if(!Go->IsRouteMode())
+ if((OldRoomIndex && (CurrentRoomIndex != OldRoomIndex)) && !FirstStep)
+ {
+ Go->Terminate(false);
+ return;
+ }
+
+ for(int c = 0; c < Squares; ++c)
+ if((MoveToSquare[c]->GetCharacter() && GetTeam() != MoveToSquare[c]->GetCharacter()->GetTeam())
+ || MoveToSquare[c]->IsDangerous(this))
+ {
+ Go->Terminate(false);
+ return;
+ }
+
+ if(!Go->IsRouteMode()){
+ int OKDirectionsCounter = 0;
+
+ for(int d = 0; d < GetNeighbourSquares(); ++d)
+ {
+ lsquare* Square = GetNeighbourLSquare(d);
+
+ if(Square && CanMoveOn(Square))
+ ++OKDirectionsCounter;
+ }
+
+ if(!Go->IsWalkingInOpen())
+ {
+ if(OKDirectionsCounter > 2)
+ {
Go->Terminate(false);
return;
}
}
+ else
+ if(OKDirectionsCounter <= 2)
+ Go->SetIsWalkingInOpen(false);
+ }
+
+ square* BeginSquare = GetSquareUnder();
+
+ if(!TryMove(MoveVector, true, game::PlayerIsRunning())
+ || BeginSquare == GetSquareUnder()
+ || (!Go->IsRouteMode() && CurrentRoomIndex && (OldRoomIndex != CurrentRoomIndex)))
+ {
+ Go->Terminate(false);
+ return;
+ }
+
+ if(IsAboveUsefulItem())
+ {
+ Go->Terminate(false);
+ return;
}
game::DrawEverything();
@@ -7123,12 +7135,13 @@ void character::DisplayStethoscopeInfo(character*) const
for(int c = 0; c < BodyParts; ++c)
{
bodypart* BodyPart = GetBodyPart(c);
+ if(!BodyPart)continue;
EntryBP.Empty();
- if(BodyPart && BodyPart->GetMainMaterial()->GetConfig() == GetTorso()->GetMainMaterial()->GetConfig())
+ if(BodyPart->GetMainMaterial()->GetConfig() == GetTorso()->GetMainMaterial()->GetConfig())
{
BodyPart->GetMainMaterial()->AddName(EntryBP, UNARTICLED);
- EntryBP << " ";
+ EntryBP<<" ";
}
BodyPart->AddName(EntryBP, UNARTICLED); //this already says the material if differs from torso
Info.AddEntry(EntryBP, LIGHT_GRAY);
diff --git a/Main/Source/cmdswapweap.cpp b/Main/Source/cmdswapweap.cpp
index a936406cc..d0335fb23 100644
--- a/Main/Source/cmdswapweap.cpp
+++ b/Main/Source/cmdswapweap.cpp
@@ -107,11 +107,14 @@ void commandsystem::SaveSwapWeapons(outputfile& SaveFile)
}
void commandsystem::LoadSwapWeapons(inputfile& SaveFile)
{DBGLN;
+ ClearSwapWeapons(); //make sure it is always cleaned from memory!
+ if(game::GetCurrentSavefileVersion()<132)
+ return;
+
SaveFile >> reinterpret_cast(iSwapCurrentIndex);
int iSize=0;
SaveFile >> reinterpret_cast(iSize); DBG2(iSwapCurrentIndex,iSize);
- ClearSwapWeapons();
for(int i=0;iGetSquareUnder()==Char->GetSquareUnder())
+ cW = colAtPlayerSquare;
+ else
+ cW = colNotOnInv; //inaccessible
// if(it!=wL && it!=wR && !hasItem(iv,it))cW = colNotOnInv;
@@ -395,7 +403,7 @@ truth commandsystem::SwapWeaponsWork(character* Char, int iIndexOverride)
if(Arm && it){
std::vector
- iv;
stk->FillItemVector(iv);
- if(hasItem(iv,it)){
+ if(hasItem(iv,it) || it->GetSquareUnder()==Char->GetSquareUnder()){
it->RemoveFromSlot(); // w/o this line of code (TODO mem gets corrupted?), it will SEGFAULT when saving the game! extremelly hard to track!!! TODO it is hard to track right?
h->SetEquipment(awRL[iArm],it);
bDidSwap=true;
diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp
index c1253b533..3d2787818 100644
--- a/Main/Source/command.cpp
+++ b/Main/Source/command.cpp
@@ -15,6 +15,7 @@
#include "char.h"
#include "command.h"
#include "database.h"
+#include "devcons.h"
#include "felist.h"
#include "game.h"
#include "god.h"
@@ -25,6 +26,7 @@
#include "message.h"
#include "miscitem.h"
#include "room.h"
+#include "specialkeys.h"
#include "stack.h"
#include "team.h"
#include "whandler.h"
@@ -71,6 +73,7 @@ command* commandsystem::Command[] =
/* Sort according to description */
new command(&Apply, "apply", 'a', 'a', 'a', false),
+ new command(&ApplyAgain, "apply last item again", 'A', 'A', 'A', false),
new command(&Talk, "chat", 'C', 'C', 'C', false),
new command(&Close, "close", 'c', 'c', 'c', false),
new command(&Dip, "dip", '!', '!', '!', false),
@@ -114,7 +117,9 @@ command* commandsystem::Command[] =
new command(&WieldInRightArm, "wield in right arm", 'w', 'w', 'w', true),
new command(&WieldInLeftArm, "wield in left arm", 'W', 'W', 'W', true),
#ifdef WIZARD
- new command(&WizardMode, "wizard mode activation", '`', '`', '`', true),
+ new command(&WizardMode, "wizard mode activation (Ctrl+ to access console commands)", '`', '`', '`', true),
+#else
+ new command(&DevConsCmd, "access console commands", '`', '`', '`', true), //works w/o Ctrl in this case
#endif
new command(&Zap, "zap", 'z', 'z', 'z', false),
@@ -144,6 +149,14 @@ command* commandsystem::Command[] =
0
};
+#ifndef WIZARD
+truth commandsystem::DevConsCmd(character* Char)
+{
+ devcons::OpenCommandsConsole();
+ return false;
+}
+#endif
+
truth commandsystem::IsForRegionListItem(int iIndex){ //see code generator helper script prepareCmdsDescrCode.sh (use cygwin)
cchar* str = Command[iIndex]->GetDescription();
if(strcmp(str,"apply")==0)return true;
@@ -554,6 +567,8 @@ truth commandsystem::Drop(character* Char)
for(uint c = 0; c < ToDrop.size(); ++c)
{
ToDrop[c]->MoveTo(Char->GetStackUnder());
+ if(ivanconfig::IsAutoPickupThrownItems())
+ ToDrop[c]->ClearTag('t'); //throw: to avoid auto-pickup
}
Success = true;
}
@@ -1039,10 +1054,7 @@ truth commandsystem::WhatToEngrave(character* Char,bool bEngraveMapNote,v2 v2Eng
}
if(game::StringQuestion(What, CONST_S("Write your map note (optionally position mouse cursor over it before editing):"), WHITE, 0, iLSqrLimit, true) == NORMAL_EXIT){
- festring finalWhat;
- finalWhat << game::MapNoteToken();
- finalWhat << What;
- lsqrN->Engrave(finalWhat);
+ game::SetMapNote(lsqrN,What);
}
break;
@@ -1314,6 +1326,8 @@ truth commandsystem::Throw(character* Char)
Char->EditExperience(PERCEPTION, 75, 1 << 8);
Char->EditNP(-50);
Char->DexterityAction(5);
+ if(ivanconfig::IsAutoPickupThrownItems())
+ Item->SetTag('t');
return true;
}
else
@@ -1322,9 +1336,37 @@ truth commandsystem::Throw(character* Char)
}
}
+ulong itLastApplyID=0; //save it?
+truth commandsystem::ApplyAgain(character* Char)
+{
+ if(itLastApplyID==0){
+ ADD_MESSAGE("I need to apply something first.");
+ return false;
+ }
+
+ item* it=game::SearchItem(itLastApplyID);
+ if(!it){
+ itLastApplyID=0;
+ ADD_MESSAGE("I can't re-apply, it was destroyed.");
+ return false;
+ }
+
+ if(it->FindCarrier()==Char){
+ ADD_MESSAGE("I will apply my %s again.",it->GetName(UNARTICLED).CStr());
+ return ApplyWork(Char,it);
+ }else
+ ADD_MESSAGE("I need to get my %s back!",it->GetName(UNARTICLED).CStr());
+
+ return false;
+}
+
truth commandsystem::Apply(character* Char)
{
+ return ApplyWork(Char);
+}
+truth commandsystem::ApplyWork(character* Char,item* itOverride)
+{
if(!Char->CanApply())
{
ADD_MESSAGE("This monster type cannot apply.");
@@ -1341,9 +1383,14 @@ truth commandsystem::Apply(character* Char)
return false;
}
- item* Item = Char->SelectFromPossessions(CONST_S("What do you want to apply?"), &item::IsAppliable);
+ item* Item = itOverride;
+ if(Item==NULL)
+ Item = Char->SelectFromPossessions(CONST_S("What do you want to apply?"), &item::IsAppliable);
bool b = Item && Item->Apply(Char);
+ if(b)
+ itLastApplyID=Item->GetID();
+
return b;
}
@@ -1487,17 +1534,45 @@ truth commandsystem::Rest(character* Char)
}
truth commandsystem::ShowMap(character* Char)
+{
+ return ShowMapWork(Char);
+}
+truth commandsystem::ShowMapWork(character* Char,v2* pv2ChoseLocation)
{
static humanoid* h;h = dynamic_cast(PLAYER);
+ bool bChoseLocationMode = pv2ChoseLocation!=NULL;
+
+ festring fsHelp;fsHelp<<
+ "[Map Help:]\n"
+ " F1 - show this message\n"
+ " Map notes containing '!' or '!!' will be highlighted.\n"
+ " Position mouse cursor over a map note to edit or delete it.\n"
+ " In look mode, clicking on a map note will navigate to that location.\n";
+
+ if(bChoseLocationMode)
+ if(!game::ToggleShowMapNotes())
+ game::ToggleShowMapNotes();
+
if( h && (h->GetLeftArm() || h->GetRightArm()) ){
if(game::ToggleDrawMapOverlay()){
lsquare* lsqrH=NULL;
while(true){
v2 noteAddPos = Char->GetPos();
- switch(game::KeyQuestion(CONST_S("Cartography notes action: (t)oggle, (e)dit/add, (l)ook mode, (r)otate, (d)elete."),
- KEY_ESC, 5, 't', 'l', 'r','d','e')
- ){
+
+ int key;
+ if(bChoseLocationMode)
+ key='l';
+ else
+ key = game::KeyQuestion(CONST_S("Cartography notes action: (t)oggle, (e)dit/add, (l)ook mode, (r)otate, (d)elete. (F1 help)"), //TODO KeyQuestion() should detect F1 and return a default answer, currently F1 will just override any other key press
+ KEY_ESC, 5, 't', 'l', 'r', 'd', 'e');
+
+ if(specialkeys::IsRequestedEvent(specialkeys::FocusedElementHelp)){
+ specialkeys::ConsumeEvent(specialkeys::FocusedElementHelp,fsHelp);
+ continue;
+ }
+
+ switch(key){
case 'd':
lsqrH = game::GetHighlightedMapNoteLSquare();
if(lsqrH!=NULL){
@@ -1515,11 +1590,30 @@ truth commandsystem::ShowMap(character* Char)
case 'l':
if(noteAddPos==Char->GetPos()){
game::RefreshDrawMapOverlay();
- noteAddPos = game::PositionQuestion(CONST_S(
- "Where do you wish to add a map note? [direction keys move cursor, space accepts]"),
- Char->GetPos(), NULL, NULL, true); DBGSV2(noteAddPos);
- if(noteAddPos==ERROR_V2)
- continue;
+
+ festring fsMsg = pv2ChoseLocation!=NULL ? "Chose a location." :
+ "Where do you wish to add a map note?";
+ fsMsg<<" [direction keys move cursor, space accepts]";
+
+ v2 start;
+ if(pv2ChoseLocation!=NULL){
+ if(!(*pv2ChoseLocation).Is0())
+ if(Char->GetLevel()->IsValidPos((*pv2ChoseLocation)))
+ start=(*pv2ChoseLocation);
+ }
+ if(start.Is0())
+ start=Char->GetPos();
+
+ noteAddPos = game::PositionQuestion(fsMsg, start, NULL, NULL, true); DBGSV2(noteAddPos);
+ if(noteAddPos==ERROR_V2){
+ game::ToggleDrawMapOverlay();
+ return false; //continue;
+ }
+ if(pv2ChoseLocation!=NULL){
+ (*pv2ChoseLocation)=noteAddPos;
+ game::ToggleDrawMapOverlay();
+ return (*pv2ChoseLocation) != Char->GetPos();
+ }
}
/* no break */
case 'e':
@@ -1550,26 +1644,99 @@ truth commandsystem::Sit(character* Char)
return (Square->GetOLTerrain() && Square->GetOLTerrain()->SitOn(Char)) || Square->GetGLTerrain()->SitOn(Char);
}
+std::vector RouteGoOn;
+level* LevelRouteGoOn=NULL;
+v2 v2RouteTarget=v2(0,0); //TODO savegame this?
+
+std::vector commandsystem::GetRouteGoOnCopy(){
+ if(game::GetCurrentLevel()!=LevelRouteGoOn || v2RouteTarget.Is0()){
+ std::vector empty;
+ return empty;
+ }
+ return RouteGoOn;
+}
+
truth commandsystem::Go(character* Char)
{
- int Dir = game::DirectionQuestion(CONST_S("In what direction do you want to go? [press a direction key]"), false);
+ int Dir = DIR_ERROR;
+
+ if(LevelRouteGoOn!=Char->GetLevel())
+ v2RouteTarget=v2(0,0);
+
+ if(Char->GetPos()==v2RouteTarget) //TODO is near by 1 dist (2 or more may have a wall in-between)
+ v2RouteTarget=v2(0,0);
+
+ if(!v2RouteTarget.Is0()){
+ switch(game::KeyQuestion(CONST_S("Continue going thru the route? [y/n]"), KEY_ESC, 2, 'y', 'n')){
+ case 'y':
+ Dir = YOURSELF;
+ break;
+ case 'n':
+ v2RouteTarget=v2(0,0);
+ break;
+ default:
+ return false;
+ }
+ }
if(Dir == DIR_ERROR)
+ Dir = game::DirectionQuestion(CONST_S("In what direction do you want to go? [press a direction key or '.' for map route]"), false, true);
+
+ if(Dir == DIR_ERROR)
+ return false;
+
+ RouteGoOn.clear();
+ if(Dir == YOURSELF){
+ if(v2RouteTarget.Is0())
+ if(!ShowMapWork(Char,&v2RouteTarget)){
+ v2RouteTarget=v2(0,0);
+ return false;
+ }
+
+ if(Char->GetPos()==v2RouteTarget){
+ v2RouteTarget=v2(0,0);
+ return false;
+ }
+
+ std::set Illegal;
+ node* Node = Char->GetLevel()->FindRoute(Char->GetPos(), v2RouteTarget, Illegal, 0, Char);
+ if(Node){
+ RouteGoOn.clear();
+ while(Node->Last)
+ {
+ RouteGoOn.push_back(Node->Pos);
+ Node = Node->Last;
+ }
+ }
+ }
+
+ if(Dir == YOURSELF && RouteGoOn.size()==0){
+ v2RouteTarget=v2(0,0);
return false;
+ }
go* Go = go::Spawn(Char);
- Go->SetDirection(Dir);
- int OKDirectionsCounter = 0;
+ if(Dir == YOURSELF){
+ Go->SetRoute(RouteGoOn);
+ Go->SetDirectionFromRoute();
+ Go->SetIsWalkingInOpen(true); //prevents stopping on path crosses/forks
+ LevelRouteGoOn=Char->GetLevel();
+ }else{
+ Go->SetDirection(Dir);
- for(int d = 0; d < Char->GetNeighbourSquares(); ++d)
- {
- lsquare* Square = Char->GetNeighbourLSquare(d);
+ int OKDirectionsCounter = 0;
+
+ for(int d = 0; d < Char->GetNeighbourSquares(); ++d)
+ {
+ lsquare* Square = Char->GetNeighbourLSquare(d);
+
+ if(Square && Char->CanMoveOn(Square))
+ ++OKDirectionsCounter;
+ }
- if(Square && Char->CanMoveOn(Square))
- ++OKDirectionsCounter;
+ Go->SetIsWalkingInOpen(OKDirectionsCounter > 2);
}
- Go->SetIsWalkingInOpen(OKDirectionsCounter > 2);
Char->SetAction(Go);
Char->EditAP(Char->GetStateAPGain(100)); // gum solution
Char->GoOn(Go, true);
diff --git a/Main/Source/definesvalidator.cpp b/Main/Source/definesvalidator.cpp
new file mode 100644
index 000000000..e39a9d634
--- /dev/null
+++ b/Main/Source/definesvalidator.cpp
@@ -0,0 +1,145 @@
+/*
+ *
+ * Iter Vehemens ad Necem (IVAN)
+ * Copyright (C) Timo Kiviluoto
+ * Released under the GNU General
+ * Public License
+ *
+ * See LICENSING which should be included
+ * along with this file for more details
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "proto.h"
+#include "devcons.h"
+#include "game.h"
+#include "message.h"
+
+//static void DefinesValidatorAppend(std::string s);
+//static void DefinesValidatorTop();
+//static void DefinesValidatorAppendCode(std::string s);
+std::ofstream DefinesValidator;
+void DefinesValidatorAppend(std::string s)
+{
+ static std::stringstream ssValidateLine;ssValidateLine.str(std::string());ssValidateLine.clear(); //actually clear/empty it = ""
+
+ ssValidateLine << s << std::endl;
+
+ static bool bDummyInit = [](){
+ DefinesValidator.open(
+ festring(game::GetHomeDir() + "definesvalidator.h").CStr(),
+ std::ios::binary);
+ return true;}();
+
+ DefinesValidator.write(ssValidateLine.str().c_str(),ssValidateLine.str().length());
+}
+void DefinesValidatorTop()
+{
+ DefinesValidatorAppend("/****");
+ DefinesValidatorAppend(" * AUTO-GENERATED CODE FILE, DO NOT MODIFY as modifications will be overwritten !!!");
+ DefinesValidatorAppend(" *");
+ DefinesValidatorAppend(" * After it is generated, update the one at source code path with it and");
+ DefinesValidatorAppend(" * recompile so the results on the abort message (if happens) will be updated !!!");
+ DefinesValidatorAppend(" */");
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend("#ifndef _DEFINESVALIDATOR_H_");
+ DefinesValidatorAppend("#define _DEFINESVALIDATOR_H_");
+ DefinesValidatorAppend("");
+
+ #define INCDEPS(dep) DefinesValidatorAppend("#include <"#dep">");
+ INCDEPS(string);
+ INCDEPS(algorithm);
+ INCDEPS(cstdarg);
+ INCDEPS(string);
+ INCDEPS(sstream);
+ INCDEPS(iostream);
+ INCDEPS(vector);
+ INCDEPS(bitset);
+ INCDEPS(ctime);
+
+ DefinesValidatorAppend("");
+
+ #define INCDEPSH(dep) DefinesValidatorAppend("#include \""#dep"\"");
+ INCDEPSH(error.h)
+
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend("class definesvalidator{");
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend(" public:");
+ DefinesValidatorAppend(" static void init();");
+ DefinesValidatorAppend(" static void DevConsCmd(std::string);");
+ DefinesValidatorAppend(" static void GenerateDefinesValidator(std::string);");
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend(" static void Validate() {");
+ DefinesValidatorAppend(" std::stringstream ssErrors;");
+ DefinesValidatorAppend(" std::bitset<32> bsA, bsB;");
+ DefinesValidatorAppend("");
+}
+void DefinesValidatorAppendCode(std::string sDefineId, long valueReadFromDatFile)
+{
+ static std::stringstream ssMsg;ssMsg.str(std::string());ssMsg.clear(); //actually clear/empty it = ""
+
+ ssMsg << "\"Defined " << sDefineId << " with value " << valueReadFromDatFile << " from .dat file " <<
+ "mismatches hardcoded c++ define value of \" << " << sDefineId << " << \"!\"";
+
+
+ static std::stringstream ssCode;ssCode.str(std::string());ssCode.clear(); //actually clear/empty it = ""
+
+// " if( " << valueReadFromDatFile << " != ((ulong)" << sDefineId << ") ) // DO NOT MODIFY!" << std::endl <<
+ ssCode <<
+ " " << std::endl <<
+ "#ifdef " << sDefineId << " // DO NOT MODIFY!" << std::endl <<
+ " bsA = " << valueReadFromDatFile << ";" << std::endl <<
+ " bsB = " << sDefineId << ";" << std::endl <<
+ " if(bsA!=bsB)" << std::endl <<
+ " ssErrors << " << ssMsg.str() << " << std::endl;" << std::endl <<
+ "#endif " << std::endl;
+
+
+ DefinesValidatorAppend(ssCode.str());
+}
+void DefinesValidatorClose(){
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend(" if(ssErrors.str().length() > 0) ABORT(ssErrors.str().c_str());");
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend("}};");
+ DefinesValidatorAppend("");
+ DefinesValidatorAppend("#endif // _DEFINESVALIDATOR_H_");
+
+ DefinesValidator.close();
+}
+#include "definesvalidator.h" //tip: 1st run this was commented
+void CmdDevConsGenDefVal(std::string strOpt){
+ definesvalidator::GenerateDefinesValidator(strOpt);
+}
+void definesvalidator::init(){
+ devcons::AddDevCmd("DefVal", CmdDevConsGenDefVal,
+ " generate the validator at user config path or validate the file 'define.dat' (may abort)");
+}
+void definesvalidator::GenerateDefinesValidator(std::string strOpt)
+{
+ if(strOpt=="generate"){
+ DefinesValidatorTop();
+
+ for(const valuemap::value_type& p : game::GetGlobalValueMap())
+ DefinesValidatorAppendCode(p.first.CStr(), p.second);
+
+ DefinesValidatorClose();
+ ADD_MESSAGE("generated the defines validator");
+ }else
+ if(strOpt=="validate"){
+ definesvalidator::Validate(); //tip: 1st run this was commented
+ ADD_MESSAGE("validated 'defines.dat'");
+ }else{
+ ADD_MESSAGE("invalid option: '%s'",strOpt.c_str());
+ }
+}
diff --git a/Main/Source/devcons.cpp b/Main/Source/devcons.cpp
new file mode 100644
index 000000000..1bcd2fbed
--- /dev/null
+++ b/Main/Source/devcons.cpp
@@ -0,0 +1,373 @@
+/*
+ *
+ * Iter Vehemens ad Necem (IVAN)
+ * Copyright (C) Timo Kiviluoto
+ * Released under the GNU General
+ * Public License
+ *
+ * See LICENSING which should be included
+ * along with this file for more details
+ *
+ */
+
+#include
+#include
+
+#include "SDL.h"
+
+//#include "bugworkaround.h"
+#include "char.h"
+#include "devcons.h"
+#include "error.h"
+#include "feio.h"
+#include "felist.h"
+#include "game.h"
+#include "message.h"
+#include "stack.h"
+#include "specialkeys.h"
+
+/**
+ * ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!
+ * ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!
+ * ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!
+ *
+ * Non-wizard commands are intended ONLY to easify fixing the game,
+ * help the player get un-stuck if ever happens,
+ * workaround some glitch like a blocked path that shouldnt be,
+ * etc.
+ *
+ * Any information/functionality that provides an advantage must be considered a cheat, therefore WIZARD MODE!!!
+ */
+
+#ifdef WIZARD
+void ListChars(std::string strFilter){
+ ulong idFilter=0;
+ if(!strFilter.empty())
+ idFilter=atoi(strFilter.c_str());
+
+ DEVCMDMSG("params: %d",idFilter);
+
+// std::vector vc = game::GetAllCharacters();
+// for(int i=0;isecond;
+ if(idFilter!=0 && idFilter!=C->GetID())continue;
+
+ festring fsPos="NULL";
+ if(C->GetSquareUnder()!=NULL){
+ fsPos.Empty();
+ fsPos<GetPos().X<<","<GetPos().Y;
+ }
+
+ festring fsMsg;
+ fsMsg << (C->IsPlayer()?"@":" ") <<
+ "id="<GetID()<<"["<first<<"] "<<
+ "("<GetName(DEFINITE)<<"'";
+ character* PB = C->GetPolymorphBackup();
+ if(PB!=NULL)
+ fsMsg << " PB='"<GetID() <<"/"<< PB->GetName(DEFINITE)<<"'";
+ fsMsg << ".";
+ DEVCMDMSG("%s",fsMsg.CStr());
+// DEVCMDMSG("%sid=%d[%d] (%s) '%s'.",
+//// ADD_MESSAGE("%sid=%d (%d,%d) '%s'.",
+// C->IsPlayer()?"@":" ",
+// C->GetID(),
+// itr->first,
+// fsPos.CStr(),
+// C->GetName(DEFINITE).CStr()
+// );
+ }
+}
+void ListItems(std::string strParams){
+ ulong idCharFilter=0;
+ ulong idFilter=0;
+
+ if(!strParams.empty()){
+ std::string part;
+ std::stringstream iss(strParams);
+ if(iss >> part){
+ if(part=="c"){
+ if(iss >> part)
+ idCharFilter=atoi(part.c_str());
+ }
+ if(part=="i"){
+ if(iss >> part)
+ idFilter=atoi(part.c_str());
+ }
+ }
+ }
+
+ DEVCMDMSG("params: %d %d",idFilter,idCharFilter);
+
+ itemidmap map = game::GetItemIDMapCopy();
+ for(itemidmap::iterator itr = map.begin();itr!=map.end();itr++){
+// return Iterator != ItemIDMap.end() ? Iterator->second : 0;
+// std::vector
- vc = game::GetAllItems();
+// for(int i=0;isecond; //helps on debugging too
+
+ //TODO could check app memory range if pointer is within limits...
+ if( //TODO items could have a random key to detect if they were not deleted improperly or w/e, could still segfault when reading it tho...
+ it->GetVolume()==0 ||
+ it->GetID()==0 ||
+ it->GetSquaresUnder()==0 ||
+ it->GetSquaresUnder()>100 || //something improbable, could be just 8 I guess...
+ false
+ ){
+ DEVCMDMSG("item REFERENCE INVALID at consistent list ID=%d 0x%X",itr->first,it); //was the item deleted or what happened?
+ }
+
+ if(idFilter!=0 && idFilter!=it->GetID())
+ continue;
+
+ slot* Slot = it->GetSlot();
+
+ const entity* ent;
+ festring fsType;
+ if(dynamic_cast(Slot)!=NULL){
+ ent=((gearslot*)Slot)->FindCarrier();
+ fsType="gear";
+ }else
+ if(dynamic_cast(Slot)!=NULL){
+ ent=((bodypartslot*)Slot)->GetMaster();
+ fsType="bodypart";
+ }else
+ if(dynamic_cast(Slot)!=NULL){
+ stackslot* sl = ((stackslot*)Slot);
+ ent=sl->FindCarrier();
+ if(sl->GetMotherStack()!=NULL)
+ ent=sl->GetMotherStack()->GetMotherEntity();
+ fsType="stack";
+ }else
+ ent=NULL;
+
+ festring fsDec;
+ citem* entI;
+ ccharacter* entC;
+ if(dynamic_cast(ent)){
+ entI=(citem*)ent;
+ entC=NULL;
+ if(dynamic_cast(ent)){
+ const corpse* CP = (const corpse*)ent;
+ entC = CP->GetDeceased();
+ fsDec=",Dec";
+ }
+ }else
+ if(dynamic_cast(ent)){
+ entI=NULL;
+ entC=(ccharacter*)ent;
+ }else{
+ entI=NULL;
+ entC=NULL;
+ }
+
+ if(idCharFilter!=0)
+ if(entC==NULL || entC->GetID()!=idCharFilter)
+ continue;
+
+ bool bPlayerStuff = entC!=NULL && entC->IsPlayer();
+
+ festring fsPB;
+ if(entC!=NULL && entC->GetPolymorphBackup()!=NULL && entC->GetPolymorphBackup()->IsPlayer())
+ fsPB=",PB";
+
+ festring fsPos="NULL";
+ if(it->GetSquareUnder()!=NULL){
+ fsPos.Empty();
+ fsPos<GetPos().X<<","<GetPos().Y;
+ }
+
+ DEVCMDMSG("%sid=%d (%s) '%s' owner '%d/%s' '%d/%s' (%s%s%s).",
+ bPlayerStuff?"@":" ",
+ it->GetID(),
+
+ fsPos.CStr(),
+
+ it->GetName(DEFINITE).CStr(),
+
+ entC!=NULL ? entC->GetID() : 0,
+ entC!=NULL ? entC->GetName(DEFINITE).CStr() : "NoEntC",
+
+ entI!=NULL ? entI->GetID() : 0,
+ entI!=NULL ? entI->GetName(DEFINITE).CStr() : "NoEntI",
+
+ fsType.CStr(),
+ fsPB.CStr(),
+ fsDec.CStr()
+ );
+ }
+}
+#endif
+
+void devcons::Init()
+{
+ specialkeys::AddCtrlOrFuncKeyHandler(SDLK_BACKQUOTE,&OpenCommandsConsole);
+}
+
+const int iVarTot=10;
+float afVars[iVarTot];
+void devcons::SetVar(std::string strParams)
+{
+ if(!strParams.empty()){
+ std::string part;
+ std::stringstream iss(strParams);
+
+ iss >> part;
+ int index = atoi(part.c_str()); //TODO use string IDs instead of index and create a map
+ if(index<0 || index>=iVarTot)
+ ABORT("invalid var index %d",index);
+
+ iss >> part;
+ int value = atof(part.c_str());
+
+ afVars[index]=value;
+ }
+}
+float devcons::GetVar(int index,float fDefaultIf0)
+{
+ static bool bDummyInit = [](){for(int i=0;i=iVarTot)
+ ABORT("invalid var index %d",index);
+ return afVars[index] == 0 ? fDefaultIf0 : afVars[index];
+}
+
+bool bOpenCommandsConsole=false;
+void devcons::OpenCommandsConsole()
+{
+ static bool bDummyInit = [](){
+ #define ADDCMD(cm,sh,bw) AddDevCmd(#cm,cm,sh,bw);
+ ADDCMD(Help,"show this help",false);
+ AddDevCmd("?",Help,"show this help",false);
+#ifdef WIZARD
+ ADDCMD(SetVar,festring()<<" set a float variable index (max "<<(iVarTot-1)<<") to be used on debug",true);
+ ADDCMD(ListChars,"[filterCharID:ulong] list all characters on current dungeon level",true);
+ ADDCMD(ListItems,"[[c|i] ] filter by char or item ID. List all items on current dungeon level, including on characters inventory and containers",true);
+#endif
+ return true;
+ }();
+
+ // if(felist::isAnyFelistCurrentlyDrawn()) return;
+ // if(iosystem::IsInUse()) return;
+ // if(iosystem::IsOnMenu()) return;
+ // if(game::IsQuestionMode()) return;
+ // if(game::IsInGetCommand()) return;
+ // if(specialkeys::IsConsumingEvent()) return;
+ // if(bugfixdp::IsAlertConfirmFixMsgDraw) return;
+ /**
+ * can only be opened if nothing else is being done,
+ * if waiting some global player command,
+ * this is better than all other specific checks.
+ */
+ if(!game::IsInGetCommand())
+ return;
+
+ if(bOpenCommandsConsole)
+ return;
+
+ bOpenCommandsConsole=true;
+ for(;;){
+ static festring fsFullCmd;
+ festring fsQ;
+ if(game::WizardModeIsActive())
+ fsQ="Developer(WIZ) ";
+ fsQ<<"Console Command (try 'help' or '?'):";
+ //TODO key up/down commands history and save/load to a txt file
+ if(game::StringQuestion(fsFullCmd, fsQ, WHITE, 1, 255, true) == NORMAL_EXIT){
+ runCommand(fsFullCmd);
+ msgsystem::DrawMessageHistory();
+ }else
+ break;
+ }
+ bOpenCommandsConsole=false;
+}
+
+typedef void (*callcmd)(std::string);
+
+struct DevCmd{
+ std::string strCmd="";
+ std::string strCmdLowerCase="";
+ std::string strHelp="";
+ callcmd Call=NULL;
+ bool bWizardModeOnly=false;
+};
+
+std::vector vCmd;
+
+void devcons::AddDevCmd(festring fsCmd, callcmd Call, festring fsHelp,bool bWizardModeOnly)
+{
+ callcmd cc = Find(fsCmd.CStr());
+ if(cc!=NULL) //TODO ignore if equal?
+ ABORT("command %s already set %x %x",fsCmd.CStr(),cc,Call);
+
+ DevCmd dc;
+ dc.strCmd=fsCmd.CStr();
+ dc.strCmdLowerCase=fsCmd.CStr();
+ std::transform(dc.strCmdLowerCase.begin(), dc.strCmdLowerCase.end(), dc.strCmdLowerCase.begin(), ::tolower);
+ dc.strHelp=fsHelp.CStr();
+ dc.Call = Call;
+ dc.bWizardModeOnly=bWizardModeOnly;
+
+ int i=dc.strCmd.find(" ");
+ if(i!=std::string::npos)
+ ABORT("command must not contain spaces '%s'",dc.strCmd.c_str());
+
+ vCmd.push_back(dc);
+}
+
+void devcons::Help(std::string strFilter)
+{
+ for(int j=0;jsecond == NULL){
+ if(!bugfixdp::IsFixing())
+ ABORT("AlreadyErased:CharacterID %d",ID);
+ }else
+ CharacterIDMap.erase(itr);
}
void game::AddItemID(item* Item, ulong ID)
{
@@ -293,15 +308,20 @@ void game::RemoveItemID(ulong ID)
{
if(ID){
DBG2("Erasing:ItemID",ID);
-// if(ID==20957)DBGSTK;//temp test case debug
- DBGEXEC(
- if(SearchItem(ID)==NULL){
- DBG2("AlreadyErased:ItemID",ID); //TODO ABORT?
- DBGSTK;
- }
- );
- ItemIDMap.erase(ItemIDMap.find(ID));
- DBG2("ERASED!:ItemID",ID);
+
+ itemidmap::iterator itr = ItemIDMap.find(ID); //TODO if the search affects performance, make this optional
+ if(itr == ItemIDMap.end() || itr->second == NULL){
+ /**
+ * This happens when the duplicated player bug happens!
+ * so it will try to erase the item 2 times and CRASH on the second,
+ * therefore the abort is appropriate.
+ */
+ if(!bugfixdp::IsFixing())
+ ABORT("AlreadyErased:ItemID %d, possible dup char bug",ID);
+ }else{
+ ItemIDMap.erase(itr);
+ DBG2("ERASED!:ItemID",ID);
+ }
}
}
@@ -315,7 +335,14 @@ void game::AddTrapID(entity* Trap, ulong ID)
}
void game::RemoveTrapID(ulong ID)
{
- if(ID) TrapIDMap.erase(TrapIDMap.find(ID));
+ if(ID){
+ trapidmap::iterator itr = TrapIDMap.find(ID);
+ if(itr == TrapIDMap.end() || itr->second == NULL){
+ if(!bugfixdp::IsFixing())
+ ABORT("AlreadyErased:TrapID %d",ID);
+ }else
+ TrapIDMap.erase(itr);
+ }
}
void game::UpdateTrapID(entity* Trap, ulong ID)
{
@@ -411,8 +438,15 @@ void game::PrepareToClearNonVisibleSquaresAround(v2 v2SqrPos) {
v2BottomRight=v2ChkSqrPos; //it will keep updating bottom right while it can
plsqChk = plv->GetLSquare(v2ChkSqrPos);
- if(plsqChk->CanBeSeenByPlayer())continue;DBGLN;
- if(!IsInWilderness() && plsqChk->CanBeFeltByPlayer())continue;DBGLN;
+ if(plsqChk->CanBeSeenByPlayer())
+ continue;
+ if(!IsInWilderness()){
+ if(plsqChk->CanBeFeltByPlayer())
+ continue;
+ if(plsqChk->GetCharacter()!=NULL)
+ if(plsqChk->GetCharacter()->CanBeSeenByPlayer())
+ continue;
+ }
/********************************************************************************************
* Now the final thing is to setup the relative pixel position on the small blitdata->bitmap
@@ -829,7 +863,8 @@ truth game::Init(cfestring& loadBaseName)
GameBegan = time(0);
LastLoad = time(0);
TimePlayedBeforeLastLoad = time::GetZeroTime();
- commandsystem::ClearSwapWeapons();
+ commandsystem::ClearSwapWeapons(); //to clear the memory from possibly previously loaded game
+//TODO craftcore::SetSuspended(NULL); //to clear the memory from possibly previously loaded game
bool PlayerHasReceivedAllGodsKnownBonus = false;
ADD_MESSAGE("You commence your journey to Attnam. Use direction keys to "
"move, '>' to enter an area and '?' to view other commands.");
@@ -1192,6 +1227,14 @@ truth game::OnScreen(v2 Pos)
&& Pos.X < GetCamera().X + GetScreenXSize() && Pos.Y < GetCamera().Y + GetScreenYSize();
}
+void game::SetMapNote(lsquare* lsqrN,festring What)
+{
+ festring finalWhat;
+ finalWhat << game::MapNoteToken();
+ finalWhat << What;
+ lsqrN->Engrave(finalWhat);
+}
+
bool bDrawMapOverlayEnabled=false;
int iMapOverlayDrawCount=0;
bool game::ToggleDrawMapOverlay()
@@ -1248,7 +1291,80 @@ int game::RotateMapNotes()
return iMapNotesRotation;
}
-bool bShowMapNotes=false;
+int game::CheckAutoPickup(square* sqr)
+{
+ if(!ivanconfig::IsAutoPickupThrownItems())
+ return false;
+
+ if(sqr==NULL)
+ sqr = PLAYER->GetSquareUnder();
+
+ if(dynamic_cast(sqr)==NULL)
+ return false;
+
+ lsquare* lsqr = (lsquare*)sqr;
+
+ itemvector iv;
+ lsqr->GetStack()->FillItemVector(iv);
+ int j=0;
+ for(int i=0;iHasTag('t')){ //throw
+ it->MoveTo(PLAYER->GetStack());
+ ADD_MESSAGE("%s picked up.", it->GetName(INDEFINITE).CStr());
+ j++;
+ }
+ }
+
+ return j;
+}
+
+bool game::CheckAddAutoMapNote(square* sqr)
+{
+ if(sqr==NULL)
+ sqr = PLAYER->GetSquareUnder();
+
+ if(dynamic_cast(sqr)==NULL)
+ return false;
+
+ lsquare* lsqr = (lsquare*)sqr;
+
+ if(lsqr->GetEngraved())
+ return false;
+
+ olterrain* olt = lsqr->GetOLTerrain();
+ if(!olt)return false;
+
+ festring fs;
+ if(fs.GetSize()==0 && dynamic_cast(olt)!=NULL)
+ fs<GetMasterGod()->GetName()<<" altar";
+ if(fs.GetSize()==0 && dynamic_cast(olt)!=NULL)
+ fs<<"Sign: "<<((sign*)olt)->GetText();
+
+ if(
+ dynamic_cast(olt)!=NULL ||
+ dynamic_cast(olt)!=NULL ||
+ dynamic_cast(olt)!=NULL ||
+ dynamic_cast(olt)!=NULL ||
+ olt->GetConfig() == ANVIL ||
+ olt->GetConfig() == FORGE ||
+ olt->GetConfig() == WORK_BENCH ||
+ false
+ ){
+ olt->AddName(fs,INDEFINITE);
+// fs<GetNameSingular();
+ }
+
+ if(fs.GetSize()>0){
+ SetMapNote(lsqr,fs);
+ game::RefreshDrawMapOverlay();
+ return true;
+ }
+
+ return false;
+}
+
+bool bShowMapNotes=true;
bool game::ToggleShowMapNotes()
{
bShowMapNotes=!bShowMapNotes;
@@ -1277,6 +1393,8 @@ col16 colMapNoteBkg;
int iNoteHighlight=-1;
lsquare* game::GetHighlightedMapNoteLSquare()
{DBGLN;
+ if(!bDrawMapOverlayEnabled)return NULL;
+ if(!bShowMapNotes)return NULL;
if(iNoteHighlight==-1)return NULL;DBGLN;
if(iNoteHighlight>=vMapNotes.size())return NULL;DBGLN;
return vMapNotes[iNoteHighlight].lsqr; //no need to expose mapnote, all info required is at lsqr
@@ -1367,7 +1485,14 @@ void game::DrawMapNotesOverlay(bitmap* buffer)
// col16 colBkg = iNoteHighlight==i ? colBkg=YELLOW : colMapNoteBkg;
if(validateV2(bkgTL,buffer,bkgB)){
- buffer->Fill(bkgTL,bkgB,colMapNoteBkg); //bkg
+ col16 colMapNoteBkg2=colMapNoteBkg;
+ if(festring(vMapNotes[i].note).Find("!!")!=festring::NPos)
+ colMapNoteBkg2=RED;
+ else
+ if(festring(vMapNotes[i].note).Find("!")!=festring::NPos)
+ colMapNoteBkg2=BLUE;
+
+ buffer->Fill(bkgTL,bkgB,colMapNoteBkg2); //bkg
buffer->DrawRectangle(bkgTL,bkgTL+bkgB,LIGHT_GRAY,iNoteHighlight==i); //bkg
}
@@ -1382,8 +1507,10 @@ void game::DrawMapNotesOverlay(bitmap* buffer)
}
for(int i=0;iDrawLine(vMapNotes[i].scrPos, vMapNotes[i].v2LineHook, ac[i%iTotCol], iNoteHighlight==i);
+ if(validateV2(vMapNotes[i].scrPos,buffer) && validateV2(vMapNotes[i].v2LineHook,buffer)){
+ bool bNH = iNoteHighlight==i;
+ buffer->DrawLine(vMapNotes[i].scrPos, vMapNotes[i].v2LineHook, bNH ? WHITE : ac[i%iTotCol], bNH);
+ }
}
for(int i=0;iPrintf(buffer, vMapNotes[i].basePos, WHITE, "%s", vMapNotes[i].note);
}
+const char* cHugeMap="I can't open a map that is as big as the world!";
void game::DrawMapOverlay(bitmap* buffer)
{ DBGLN;
if(!bDrawMapOverlayEnabled)return;
if(ivanconfig::GetStartingDungeonGfxScale()==1){
- ADD_MESSAGE("This map is as big as the world!");
+ ADD_MESSAGE(cHugeMap);
bDrawMapOverlayEnabled=false;
return;
}
@@ -1437,6 +1565,8 @@ void game::DrawMapOverlay(bitmap* buffer)
static v2 v2MapScrSizeFinal(0,0);
static bitmap* bmpFinal;
+ bool bTransparentMap = bPositionQuestionMode && (CursorPos != PLAYER->GetPos()) && ivanconfig::IsTransparentMapLM();
+
if(bPositionQuestionMode){
static v2 v2PreviousCursorPos;
if(v2PreviousCursorPos != CursorPos){
@@ -1516,12 +1646,14 @@ void game::DrawMapOverlay(bitmap* buffer)
delete bmpMapBuffer;
bmpMapBuffer=new bitmap(v2BmpSize);
}
-// bmpMapBuffer->ClearToColor(TRANSPARENT_COLOR);
+// bmpMapBuffer->ClearToColor(TRANSPARENT_COLOR);
+ bmpMapBuffer->ClearToColor(BLACK);
v2 v2PlayerScrPos(0,0);
v2 v2CursorScrPos(-1,-1);
v2 v2Dest(0,0);
vMapNotes.clear();
+ std::vector RouteGoOn = commandsystem::GetRouteGoOnCopy();
for(int iY=v2Min.Y;iY<=v2Max.Y;iY++){
// B.Dest.Y = v2TopLeft.Y +iY*iMapTileSize;
v2Dest.Y = (iY-v2Min.Y)*iMapTileSize;
@@ -1572,16 +1704,18 @@ void game::DrawMapOverlay(bitmap* buffer)
return true;
}();
+ bool bDrawSqr=true;
col16 colorO;
- if(lsqr->HasBeenSeen()){
- static col16 colorDoor =MakeRGB16(0xFF*0.66, 0xFF*0.33, 0); //brown
- static col16 colorFountain=MakeRGB16( 0, 0,0xFF ); //blue
- static col16 colorUp =MakeRGB16( 0, 0xFF , 0); //green
- static col16 colorDown =MakeRGB16( 0, 0xFF*0.50, 0); //dark green
- static col16 colorAltar =MakeRGB16(0xFF*0.50, 0,0xFF ); //purple
- static col16 colorNote =MakeRGB16(0xFF*0.90, 0xFF*0.90,0xFF*0.90); //just not white TODO why?
+ static col16 colorDoor =MakeRGB16(0xFF*0.66, 0xFF*0.33, 0); //brown
+ static col16 colorFountain =MakeRGB16( 0, 0,0xFF ); //blue
+ static col16 colorUp =MakeRGB16( 0, 0xFF , 0); //green
+ static col16 colorDown =MakeRGB16( 0, 0xFF*0.50, 0); //dark green
+ static col16 colorAltar =MakeRGB16(0xFF*0.50, 0,0xFF ); //purple
+ static col16 colorNote =MakeRGB16(0xFF*0.90, 0xFF*0.90,0xFF*0.90); //just not white because white is used as look mode indicator on map
+// static col16 colorGoOnRoute=MakeRGB16(0xFF*0.75, 0xFF*0.75,0xFF*0.75); //light gray
+ static col16 colorGoOnRoute=MakeRGB16( 0, 0xFF*0.75,0xFF ); //cyan
// static col16 colorOnGround=MakeRGB16(0xFF*0.80, 0xFF*0.50,0xFF*0.20); //orange
-
+ if(lsqr->HasBeenSeen()){
static const int iTotRM=5 +1; //5 is max rest modifier from dat files
static col16 colorOnGroundRM[iTotRM];
static bool bDummyInit2 = [](){
@@ -1628,6 +1762,7 @@ void game::DrawMapOverlay(bitmap* buffer)
}
}else{ //floor
colorO=colorFloor;
+ if(bTransparentMap)bDrawSqr=false;
}
if(lsqr->IsMaterialDetected()) //color override
@@ -1635,9 +1770,25 @@ void game::DrawMapOverlay(bitmap* buffer)
}else{
colorO=colorMapBkg;
+ if(bTransparentMap)bDrawSqr=false;
}
- bmpMapBuffer->Fill(v2Dest, v2MapTileSize, colorO);
+ if(RouteGoOn.size()>0)
+ for(auto v2Rt = RouteGoOn.begin(); v2Rt != RouteGoOn.end(); v2Rt++)
+ if(v2SqrPos == *v2Rt){
+ colorO=colorGoOnRoute;
+ bDrawSqr=true;
+ break;
+ }
+// for(std::list::iterator itrRt = RouteGoOn.begin(); itrRt != RouteGoOn.end(); itrRt++)
+// if(itrRt->second == v2SqrPos){
+// colorO=colorGoOnRoute;
+// bDrawSqr=true;
+// break;
+// }
+
+ if(bDrawSqr)
+ bmpMapBuffer->Fill(v2Dest, v2MapTileSize, colorO);
if(CursorPos == v2SqrPos)
v2CursorScrPos=v2Dest;
@@ -1766,8 +1917,18 @@ void game::DrawMapOverlay(bitmap* buffer)
v2MapSize = v2MapScrSizeFinal;
}
- bmpFinal->FastBlit(buffer, v2TopLeftFinal);
- graphics::DrawRectangleOutlineAround(buffer, v2TopLeftFinal, v2MapScrSizeFinal, LIGHT_GRAY, true);
+ static blitdata BFinal = DEFAULT_BLITDATA;
+ BFinal.Bitmap = buffer;
+ BFinal.Dest = v2TopLeftFinal;
+ BFinal.Border = bmpFinal->GetSize();
+ BFinal.MaskColor = BLACK;
+ if(bTransparentMap){
+ bmpFinal->NormalMaskedBlit(BFinal);
+ }else
+ bmpFinal->FastBlit(BFinal.Bitmap, BFinal.Dest );
+
+ if(!bTransparentMap)
+ graphics::DrawRectangleOutlineAround(buffer, v2TopLeftFinal, v2MapScrSizeFinal, LIGHT_GRAY, true);
iMapOverlayDrawCount++;
@@ -1782,7 +1943,7 @@ void DrawMapOverlayFancy(bitmap* buffer)
if(!bDrawMapOverlayEnabled)return;
if(ivanconfig::GetStartingDungeonGfxScale()==1){
- ADD_MESSAGE("This map is as big as the world!");
+ ADD_MESSAGE(cHugeMap);
}else{ //it actually work works (for now) if there is any dungeon stretching going on
if(buffer!=NULL){
static float fRGB=0.3;
@@ -3208,6 +3369,7 @@ truth game::Save(cfestring& SaveName)
protosystem::SaveCharacterDataBaseFlags(SaveFile);
commandsystem::SaveSwapWeapons(SaveFile); DBGLN;
+//TODO craftcore::Save(SaveFile);
return true;
}
@@ -3290,10 +3452,7 @@ int game::Load(cfestring& saveName)
v2 Pos;
SaveFile >> Pos >> PlayerName;
- character* CharAtPos = GetCurrentArea()->GetSquare(Pos)->GetCharacter();
- if(CharAtPos==NULL || !CharAtPos->IsPlayer())
- ABORT("Player not found! If there are backup files, try the 'restore backup' option.");
- SetPlayer( bugWorkaroundDupPlayer::BugWorkaroundDupPlayer(CharAtPos,Pos) ); DBG3(CharAtPos,Player,DBGAV2(Pos));
+ SetPlayer(bugfixdp::ValidatePlayerAt(GetCurrentArea()->GetSquare(Pos)));
msgsystem::Load(SaveFile);
SaveFile >> DangerMap >> NextDangerIDType >> NextDangerIDConfigIndex;
SaveFile >> DefaultPolymorphTo >> DefaultSummonMonster;
@@ -3303,8 +3462,10 @@ int game::Load(cfestring& saveName)
LastLoad = time(0);
protosystem::LoadCharacterDataBaseFlags(SaveFile);
- if(game::GetSaveFileVersion()>=132)
- commandsystem::LoadSwapWeapons(SaveFile);
+ commandsystem::LoadSwapWeapons(SaveFile);
+//TODO craftcore::Load(SaveFile);
+
+ ///////////////// loading ended ////////////////
UpdateCamera();
@@ -3926,8 +4087,16 @@ void game::CreateBusyAnimationCache()
}
}
+bool bQuestionMode=false;
+bool game::IsQuestionMode()
+{
+ return bQuestionMode || bPositionQuestionMode;
+}
+
int game::AskForKeyPress(cfestring& Topic)
{
+ bQuestionMode=true;
+
DrawEverythingNoBlit();
FONT->Printf(DOUBLE_BUFFER, v2(16, 8), WHITE, "%s", Topic.CapitalizeCopy().CStr());
graphics::BlitDBToScreen();
@@ -3939,6 +4108,8 @@ int game::AskForKeyPress(cfestring& Topic)
#endif
igraph::BlitBackGround(v2(16, 6), v2(GetMaxScreenXSize() << 4, 23));
+
+ bQuestionMode=false;
return Key;
}
@@ -3957,10 +4128,37 @@ v2 game::PositionQuestion(cfestring& Topic, v2 CursorPos, void (*Handler)(v2),
if(Handler)
Handler(CursorPos);
+ bool bMapNotesMode = bDrawMapOverlayEnabled && bShowMapNotes;
+
+ /**
+ * using the min millis value grants mouse will be updated most often possible
+ * default key -1 just to be ignored
+ */
+ if(bMapNotesMode)
+ globalwindowhandler::SetKeyTimeout(100,-1);
+
bPositionQuestionMode=true;
+ v2 v2PreviousClick=v2(0,0);
for(;;)
{
square* Square = GetCurrentArea()->GetSquare(CursorPos);
+
+ if(bMapNotesMode){
+ lsquare* lsqrMapNote = GetHighlightedMapNoteLSquare();
+ if(lsqrMapNote){
+ mouseclick mc = globalwindowhandler::ConsumeMouseEvent();
+ if(mc.btn==1){
+ CursorPos = lsqrMapNote->GetPos();
+ if(v2PreviousClick == CursorPos){ //the 2nd click on same pos will accept as expected TODO fast double click detection, just reset v2PreviousClick after 0.5s ?
+ Return = CursorPos;
+ break;
+ }
+ v2PreviousClick = CursorPos;
+ }
+ }
+
+ CheckAddAutoMapNote(Square);
+ }
if(!Square->HasBeenSeen()
&& (!Square->GetCharacter() || !Square->GetCharacter()->CanBeSeenByPlayer())
@@ -4037,6 +4235,9 @@ v2 game::PositionQuestion(cfestring& Topic, v2 CursorPos, void (*Handler)(v2),
}
}
+ if(bMapNotesMode)
+ globalwindowhandler::ResetKeyTimeout();
+
return Return;
}
@@ -4113,89 +4314,6 @@ truth game::AnimationController()
return true;
}
-//static void DefinesValidatorAppend(std::string s);
-//static void DefinesValidatorTop();
-//static void DefinesValidatorAppendCode(std::string s);
-std::ofstream DefinesValidator;
-void DefinesValidatorAppend(std::string s)
-{
- static std::stringstream ssValidateLine;ssValidateLine.str(std::string());ssValidateLine.clear(); //actually clear/empty it = ""
-
- ssValidateLine << s << std::endl;
-
- static bool bDummyInit = [](){
- DefinesValidator.open(
- festring(game::GetHomeDir() + "definesvalidator.h").CStr(),
- std::ios::binary);
- return true;}();
-
- DefinesValidator.write(ssValidateLine.str().c_str(),ssValidateLine.str().length());
-}
-void DefinesValidatorTop()
-{
- DefinesValidatorAppend("/****");
- DefinesValidatorAppend(" * AUTO-GENERATED CODE FILE, DO NOT MODIFY as modifications will be overwritten !!!");
- DefinesValidatorAppend(" *");
- DefinesValidatorAppend(" * After it is generated, update the one at source code path with it and");
- DefinesValidatorAppend(" * recompile so the results on the abort message (if happens) will be updated !!!");
- DefinesValidatorAppend(" */");
- DefinesValidatorAppend("");
- DefinesValidatorAppend("#ifndef _DEFINESVALIDATOR_H_");
- DefinesValidatorAppend("#define _DEFINESVALIDATOR_H_");
- DefinesValidatorAppend("");
- DefinesValidatorAppend("class definesvalidator{ public: static void Validate() {");
- DefinesValidatorAppend("");
- DefinesValidatorAppend(" std::stringstream ssErrors;");
- DefinesValidatorAppend(" std::bitset<32> bsA, bsB;");
- DefinesValidatorAppend("");
-}
-void DefinesValidatorAppendCode(std::string sDefineId, long valueReadFromDatFile)
-{
- static std::stringstream ssMsg;ssMsg.str(std::string());ssMsg.clear(); //actually clear/empty it = ""
-
- ssMsg << "\"Defined " << sDefineId << " with value " << valueReadFromDatFile << " from .dat file " <<
- "mismatches hardcoded c++ define value of \" << " << sDefineId << " << \"!\"";
-
-
- static std::stringstream ssCode;ssCode.str(std::string());ssCode.clear(); //actually clear/empty it = ""
-
-// " if( " << valueReadFromDatFile << " != ((ulong)" << sDefineId << ") ) // DO NOT MODIFY!" << std::endl <<
- ssCode <<
- " " << std::endl <<
- "#ifdef " << sDefineId << " // DO NOT MODIFY!" << std::endl <<
- " bsA = " << valueReadFromDatFile << ";" << std::endl <<
- " bsB = " << sDefineId << ";" << std::endl <<
- " if(bsA!=bsB)" << std::endl <<
- " ssErrors << " << ssMsg.str() << " << std::endl;" << std::endl <<
- "#endif " << std::endl;
-
-
- DefinesValidatorAppend(ssCode.str());
-}
-void DefinesValidatorClose(){
- DefinesValidatorAppend("");
- DefinesValidatorAppend(" if(ssErrors.str().length() > 0) ABORT(ssErrors.str().c_str());");
- DefinesValidatorAppend("");
- DefinesValidatorAppend("}};");
- DefinesValidatorAppend("");
- DefinesValidatorAppend("#endif // _DEFINESVALIDATOR_H_");
-
- DefinesValidator.close();
-}
-#include "definesvalidator.h" //tip: 1st run this was commented
-void game::GenerateDefinesValidator(bool bValidade)
-{
- DefinesValidatorTop();
-
- for(const valuemap::value_type& p : GlobalValueMap)
- DefinesValidatorAppendCode(p.first.CStr(), p.second);
-
- DefinesValidatorClose();
-
- if(bValidade)
- definesvalidator::Validate(); //tip: 1st run this was commented
-}
-
void game::InitGlobalValueMap()
{
inputfile SaveFile(GetDataDir() + "Script/define.dat", &GlobalValueMap);
@@ -4221,6 +4339,8 @@ void game::TextScreen(cfestring& Text, v2 Displacement, col16 Color,
globalwindowhandler::DisableControlLoops();
iosystem::TextScreen(Text, Displacement, Color, GKey, Fade, BitmapEditor);
globalwindowhandler::EnableControlLoops();
+ //TODO need? graphics::SetAllowStretchedBlit();
+ //TODO useful or messy? graphics::BlitDBToScreen();
}
/* ... all the keys that are acceptable
@@ -4230,6 +4350,8 @@ void game::TextScreen(cfestring& Text, v2 Displacement, col16 Color,
int game::KeyQuestion(cfestring& Message, int DefaultAnswer, int KeyNumber, ...)
{
+ bQuestionMode=true;
+
int* Key = new int[KeyNumber];
va_list Arguments;
va_start(Arguments, KeyNumber);
@@ -4260,6 +4382,8 @@ int game::KeyQuestion(cfestring& Message, int DefaultAnswer, int KeyNumber, ...)
delete [] Key;
igraph::BlitBackGround(v2(16, 6), v2(GetMaxScreenXSize() << 4, 23));
+
+ bQuestionMode=false;
return Return;
}
@@ -4325,6 +4449,8 @@ v2 game::NameKeyHandler(v2 CursorPos, int Key)
void game::End(festring DeathMessage, truth Permanently, truth AndGoToMenu)
{
+ game::SRegionAroundDisable();
+
if(!Permanently)
game::Save();
@@ -4567,16 +4693,31 @@ void game::EnterArea(charactervector& Group, int Area, int EntryIndex)
if(Player)
{
- GetCurrentLevel()->GetLSquare(Pos)->KickAnyoneStandingHereAway();
+ lsquare* lsqr = GetCurrentLevel()->GetLSquare(Pos);
+ character* NPC = lsqr->GetCharacter();
+
+ bool bMoveAway=true;
+ /**
+ * Genetrix Vesana goal is to protect the passage (or not?) TODO tho coming from above could grant a huge damage strike to help to kill it, what could be a tactical manouver
+ * using now largecreature check because of this crash stack:
+ area::GetSquare(v2) const //HERE V2 had invalid huge negative values for X and Y
+ largecreature::PutTo(v2)
+ character::PutNear(v2) //TODO some complexer code could be implemented at this method
+ lsquare::KickAnyoneStandingHereAway()
+ game::EnterArea(std::vector >&, int, int)+0x164)
+ */
+ if(bMoveAway && dynamic_cast(NPC)!=NULL)bMoveAway=false;
+
+ if(bMoveAway)
+ lsqr->KickAnyoneStandingHereAway();
+
Player->PutToOrNear(Pos);
}
else
{
- SetPlayer(GetCurrentLevel()->GetLSquare(Pos)->GetCharacter());
+ SetPlayer(bugfixdp::ValidatePlayerAt(GetCurrentLevel()->GetLSquare(Pos)));
}
- bugWorkaroundDupPlayer::BugWorkaroundDupPlayer(Player,Pos);
-
uint c;
for(c = 0; c < Group.size(); ++c)
@@ -4693,7 +4834,7 @@ void prepareList(felist& rList, v2& v2TopLeft, int& iW){
iY=v2TopLeft.Y-3;
}
- int iItemW=bldListItemTMP.Border.X*bldListItemTMP.Stretch;
+ int iItemW = bldListItemTMP.Border.X * bldListItemTMP.Stretch;
if(bAltItemPos){
iX += area::getOutlineThickness()*2; //to leave some space to alt item outline
iX += iItemW;
@@ -4706,9 +4847,11 @@ void prepareList(felist& rList, v2& v2TopLeft, int& iW){
//cant be so automatic... or user wants alt or default position... //if(bAltItemPos){iW+=iItemW;}
}
- v2TopLeft=v2(iX,iY);
+ v2TopLeft=v2(iX,iY); DBGSV2(v2TopLeft);
graphics::SetSpecialListItemAltPos(bAltItemPos);
+ if(bAltItemPos)
+ felist::SetListItemAltPosMinY(area::getTopLeftCorner().Y);
}
int prepareListWidth(int iW){
@@ -4930,6 +5073,35 @@ character* game::SearchCharacter(ulong ID)
return Iterator != CharacterIDMap.end() ? Iterator->second : 0;
}
+std::vector game::GetAllCharacters()
+{
+ std::vector vc;
+ for(int i=0;i game::GetAllItems()
+{
+ std::vector
- vc;
+ for(int i=0;iValue){
- case 0: Entry << "disabled";break;
- case 1: Entry << "missing only";break;
- case 2: Entry << "prefer old player";break;
- case 3: Entry << "prefer new player";break;
- }
-}
-
void ivanconfig::SaveGameSortModeDisplayer(const cycleoption* O, festring& Entry)
{
switch(O->Value){
@@ -863,21 +844,6 @@ void ivanconfig::FontGfxChanger(cycleoption* O, long What)
O->Value = What;
}
-void ivanconfig::GenerateDefinesValidatorChanger(truthoption* O, truth What)
-{
- if(O!=NULL)O->Value = What;
-
- if(What)
- game::GenerateDefinesValidator(true); //TODO make validation (that aborts) optional using cycleoption
-}
-
-void ivanconfig::SavegameSafelyChanger(truthoption* O, truth What)
-{
- if(O!=NULL)O->Value = What;
-
- outputfile::SetSafeSaving(What);
-}
-
void ivanconfig::XBRZScaleChanger(truthoption* O, truth What)
{
O->Value = What;
@@ -983,6 +949,7 @@ void ivanconfig::Initialize()
configsystem::AddOption(fsCategory,&WaitNeutralsMoveAway);
configsystem::AddOption(fsCategory,&EnhancedLights);
configsystem::AddOption(fsCategory,&DistLimitMagicMushrooms);
+ configsystem::AddOption(fsCategory,&AutoPickupThrownItems);
fsCategory="Window";
configsystem::AddOption(fsCategory,&Contrast);
@@ -1014,6 +981,7 @@ void ivanconfig::Initialize()
configsystem::AddOption(fsCategory,&RotateTimesPerSquare);
configsystem::AddOption(fsCategory,&HitIndicator);
configsystem::AddOption(fsCategory,&ShowMap);
+ configsystem::AddOption(fsCategory,&TransparentMapLM);
fsCategory="Sounds";
configsystem::AddOption(fsCategory,&PlaySounds);
@@ -1039,11 +1007,8 @@ void ivanconfig::Initialize()
configsystem::AddOption(fsCategory,&AllowMouseOnFelist);
fsCategory="Advanced/Developer options";
- configsystem::AddOption(fsCategory,&BugWorkaroundDupPlayer);
configsystem::AddOption(fsCategory,&AllowImportOldSavegame);
- configsystem::AddOption(fsCategory,&SavegameSafely);
configsystem::AddOption(fsCategory,&HideWeirdHitAnimationsThatLookLikeMiss);
- configsystem::AddOption(fsCategory,&GenerateDefinesValidator);
/********************************
* LOAD AND APPLY some SETTINGS *
@@ -1070,7 +1035,6 @@ void ivanconfig::Initialize()
FrameSkipChanger(NULL,FrameSkip.Value);
StackListPageLengthChanger(NULL, StackListPageLength.Value);
SaveGameSortModeChanger(NULL, SaveGameSortMode.Value);
- SavegameSafelyChanger(NULL, SavegameSafely.Value);
SelectedBkgColorChanger(NULL, SelectedBkgColor.Value);
AllowMouseOnFelistChanger(NULL, AllowMouseOnFelist.Value);
}
diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp
index 9ca4171a7..dbe4edcf6 100644
--- a/Main/Source/item.cpp
+++ b/Main/Source/item.cpp
@@ -401,6 +401,31 @@ truth item::CanBeEatenByAI(ccharacter* Eater) const
&& ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater);
}
+bool item::HasTag(char tag)
+{
+ static char Tag[3]={'#',0,0};
+ Tag[1]=tag;
+ return label.Find(Tag,0) != festring::NPos;
+}
+
+/**
+ * look for all usages to avoid tag clashes
+ */
+void item::SetTag(char tag)
+{
+ if(!HasTag(tag))
+ label<<"#"<(GetConfig());
SaveFile << static_cast(Flags);
SaveFile << Size << ID << LifeExpectancy << ItemFlags;
- if(game::GetSaveFileVersion()>=132){
- SaveFile << label;
- }
+ SaveFile << label;
SaveLinkedList(SaveFile, CloneMotherID);
if(Fluid)
@@ -447,9 +470,8 @@ void item::Load(inputfile& SaveFile)
databasecreator
- ::InstallDataBase(this, ReadType(SaveFile));
Flags |= ReadType(SaveFile) & ~ENTITY_FLAGS;
SaveFile >> Size >> ID >> LifeExpectancy >> ItemFlags;
- if(game::GetSaveFileVersion()>=132){
+ if(game::GetCurrentSavefileVersion()>=132)
SaveFile >> label;
- }
LoadLinkedList(SaveFile, CloneMotherID);
if(LifeExpectancy)
diff --git a/Main/Source/level.cpp b/Main/Source/level.cpp
index 03ddd63d8..a96d3ceed 100644
--- a/Main/Source/level.cpp
+++ b/Main/Source/level.cpp
@@ -2959,6 +2959,9 @@ int level::RevealDistantLightsToPlayer() //based on Draw() code
if(!ivanconfig::IsEnhancedLights())
return 0;
+ if(!PLAYER->GetSquareUnder()) //NULL may happen on player's death, was polymorphed when the crash happened
+ return 0;
+
cint XMin = Max(game::GetCamera().X, 0);
cint YMin = Max(game::GetCamera().Y, 0);
cint XMax = Min(XSize, game::GetCamera().X + game::GetScreenXSize());
@@ -3003,7 +3006,8 @@ int level::RevealDistantLightsToPlayer() //based on Draw() code
iMultDist=2;
if(iDist <= lMaxDist*iMultDist) //ground view limit
- if(!bTryReveal && hasLight(Square->Luminance,0xFF*0.475))bTryReveal=true; // 0.475 is based on tests with lantern
+ if(!bTryReveal && hasLight(Square->Luminance,0xFF*0.475)) // 0.475 is based on tests with lantern
+ bTryReveal=true;
}
if(bTryReveal){
diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp
index 4eef746b7..00dacceec 100644
--- a/Main/Source/main.cpp
+++ b/Main/Source/main.cpp
@@ -27,6 +27,8 @@
#include "game.h"
#include "database.h"
+#include "definesvalidator.h"
+#include "devcons.h"
#include "feio.h"
#include "igraph.h"
#include "iconf.h"
@@ -34,12 +36,15 @@
#include "hscore.h"
#include "graphics.h"
#include "script.h"
+#include "specialkeys.h"
#include "message.h"
#include "proto.h"
#include "audio.h"
#include "dbgmsgproj.h"
+#include "bugworkaround.h"
+
#ifndef WIN32
void CrashHandler(int Signal)
{
@@ -97,6 +102,10 @@ int main(int argc, char** argv)
game::CreateBusyAnimationCache();
globalwindowhandler::SetQuitMessageHandler(game::HandleQuitMessage);
globalwindowhandler::SetScrshotDirectory(game::GetScrshotDir());
+ specialkeys::init();
+ bugfixdp::init();
+ devcons::Init();
+ definesvalidator::init();
msgsystem::Init();
protosystem::Initialize();
igraph::LoadMenu();
@@ -140,7 +149,7 @@ int main(int argc, char** argv)
case 1:
{
iosystem::SetSkipSeekSave(&SkipGameScript);
- festring LoadName = iosystem::ContinueMenu(WHITE, LIGHT_GRAY, game::GetSaveDir(), game::GetSaveFileVersion(), ivanconfig::IsAllowImportOldSavegame());
+ festring LoadName = iosystem::ContinueMenu(WHITE, LIGHT_GRAY, game::GetSaveDir(), game::GetSaveFileVersionHardcoded(), ivanconfig::IsAllowImportOldSavegame());
if(LoadName.GetSize())
{
diff --git a/Main/Source/materias.cpp b/Main/Source/materias.cpp
index 551322b17..9d0628327 100644
--- a/Main/Source/materias.cpp
+++ b/Main/Source/materias.cpp
@@ -310,7 +310,8 @@ void powder::Load(inputfile& SaveFile)
material* organic::EatEffect(character* Eater, long Amount)
{
Amount = Volume > Amount ? Amount : Volume;
- GetMotherEntity()->SpecialEatEffect(Eater, Amount);
+ if(GetMotherEntity())
+ GetMotherEntity()->SpecialEatEffect(Eater, Amount);
Effect(Eater, TORSO_INDEX, Amount);
Eater->ReceiveNutrition(GetNutritionValue() * Amount * 20 / (1000 * (GetSpoilLevel() + 1)));
diff --git a/Main/Source/message.cpp b/Main/Source/message.cpp
index 0355903de..99f787556 100644
--- a/Main/Source/message.cpp
+++ b/Main/Source/message.cpp
@@ -175,6 +175,7 @@ void msgsystem::Draw()
void msgsystem::DrawMessageHistory()
{
game::RegionListItemEnable(false); //this fix the problem that happens on death
+ game::RegionSilhouetteEnable(false);
MessageHistory.SetPageLength(ivanconfig::GetStackListPageLength());
MessageHistory.Draw();
diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp
index ab31ca03c..9537d60ee 100644
--- a/Main/Source/miscitem.cpp
+++ b/Main/Source/miscitem.cpp
@@ -3018,7 +3018,7 @@ void scrollofdetectmaterial::FinishReading(character* Reader)
continue;
}
- TempMaterial = protosystem::CreateMaterial(Temp);
+ TempMaterial = protosystem::CreateMaterialForDetection(Temp);
if(TempMaterial)
break;
diff --git a/Main/Source/proto.cpp b/Main/Source/proto.cpp
index bd14704a7..a391de95e 100644
--- a/Main/Source/proto.cpp
+++ b/Main/Source/proto.cpp
@@ -473,7 +473,12 @@ item* protosystem::CreateItem(cfestring& What, truth Output)
return 0;
}
-material* protosystem::CreateMaterial(cfestring& What, long Volume, truth Output)
+material* protosystem::CreateMaterialForDetection(cfestring& What)
+{
+ return CreateMaterial(What,0,true,true);
+}
+
+material* protosystem::CreateMaterial(cfestring& What, long Volume, truth Output, truth DetectMode)
{
for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1)
{
@@ -484,11 +489,13 @@ material* protosystem::CreateMaterial(cfestring& What, long Volume, truth Output
for(int c2 = 1; c2 < ConfigSize; ++c2)
if(ConfigData[c2]->NameStem == What)
{
- if(ConfigData[c2]->CommonFlags & CAN_BE_WISHED
- || game::WizardModeIsActive())
+ if(
+ (ConfigData[c2]->CommonFlags & CAN_BE_WISHED) ||
+ (DetectMode && (ConfigData[c2]->CommonFlags & CAN_BE_DETECTED)) ||
+ game::WizardModeIsActive()
+ ){
return ConfigData[c2]->ProtoType->Spawn(ConfigData[c2]->Config, Volume);
- else if(Output)
- {
+ }else if(Output){
ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
return 0;
}
diff --git a/Script/define.dat b/Script/define.dat
index 31427a73f..01eca5b9a 100644
--- a/Script/define.dat
+++ b/Script/define.dat
@@ -1297,6 +1297,7 @@
#define CAN_BE_DESTROYED 16
#define IS_VALUABLE 32
#define CAN_BE_MIRRORED 64
+#define CAN_BE_DETECTED 128
/* NameFlags */
#define USE_AN 1
diff --git a/Script/material.dat b/Script/material.dat
index e3a030d8f..8838c4b45 100644
--- a/Script/material.dat
+++ b/Script/material.dat
@@ -50,7 +50,7 @@ material
HardenedMaterial = NONE;
SoftenedMaterial = NONE;
IntelligenceRequirement = 1;
- CommonFlags = IS_ABSTRACT|CAN_BE_WISHED|CAN_BE_MIRRORED;
+ CommonFlags = IS_ABSTRACT|CAN_BE_WISHED|CAN_BE_MIRRORED|CAN_BE_DETECTED;
NameFlags = 0;
CategoryFlags = 0;
BodyFlags = USE_MATERIAL_ATTRIBUTES;
diff --git a/Sound/SoundEffects.cfg b/Sound/SoundEffects.cfg
index 703b3e128..ca08afe47 100644
--- a/Sound/SoundEffects.cfg
+++ b/Sound/SoundEffects.cfg
@@ -75,14 +75,14 @@ DoorResists; Door Resists 1.wav, Door Resists 2.wav, Door Resists 3.wav, Door Re
DoorResists; Door Resists 1.wav, Door Resists 2.wav, Door Resists 3.wav, Door Resists 4.wav;.*door won't budge.*
##############
-### drinkf ###
+### drink fountain ###
##############
drinkf; drinkf.wav;.*dries.*up.*
drinkf; drinkf.wav;.*water.*taste.*
##############
-### drinkp ###
+### drink potion ###
##############
drinkp; drinkp.wav;.*You.*finish.*drinking.*
@@ -133,14 +133,14 @@ harp; harp.wav;.*mesmerizing sound.*
##############
### Hit ###
##############
-HitBlade; blade1.wav, blade2.wav, blade3.wav, blade4.wav, blade5.wav, blade6.wav;.*slash.*
-HitBlade; blade1.wav, blade2.wav, blade3.wav, blade4.wav, blade5.wav, blade6.wav;.*stab.*
+HitBlade; blade1.wav, blade2.wav, blade3.wav, blade4.wav, blade5.wav, blade6.wav;.* slash .*
+HitBlade; blade1.wav, blade2.wav, blade3.wav, blade4.wav, blade5.wav, blade6.wav;.* stab .*
HitBlunt; blunt1.wav, blunt2.wav, blunt3.wav, blunt4.wav, punch4.wav;.* kick .*
HitBlunt; blunt1.wav, blunt2.wav, blunt3.wav, blunt4.wav, punch4.wav;.* kicks .*
-HitBlunt; blunt1.wav, blunt2.wav, blunt3.wav, blunt4.wav, punch4.wav;.*strike.*
+HitBlunt; blunt1.wav, blunt2.wav, blunt3.wav, blunt4.wav, punch4.wav;.* strike .*
-HitMe; hit.wav, hit2.wav, hit3.wav, hit4.wav, hit5.wav;.*hits.*you.*
+HitMe; hit.wav, hit2.wav, hit3.wav, hit4.wav, hit5.wav;.* hits .* you.*
HitMisses; miss.wav, miss2.wav, miss3.wav, miss4.wav, miss5.wav;.* (miss|misses) .*
@@ -221,6 +221,8 @@ Snore; snore1.wav, snore2.wav, snore3.wav;.*You lose consciousness.*
YouFallDown; FallDown1.wav, FallDown2.wav;.*You[^.]* fall down.*
HappySounds; Woo-Hoo.wav;.*You feel tougher than anything.*
+HappySounds; Woo-Hoo.wav;.*You feel your life experience increasing all the time.*
+HappySounds; Woo-Hoo.wav;.*You feel very confident of your social skills.*
StoreItem; ContainerPutGet1.wav, ContainerPutGet2.wav, ContainerPutGet3.wav;.*You (put|take) .* (in|from) .* (chest|strong-box) .*