/* AF_Tooltips*
* Authors: Auronen & Fawkes
* Version: 0.01 alpha
*
* Description:
* This package allows you to show a tooltips, that
* explain your new amazing innovative features.
*
* Features:
* - show tooltip & hide tooltip
* - custom position per tooltip
* - timed "ShowOnce" feature - if you view a tooltip for more than 5s (customisable) it will not show again
*
*
* How to get it running:
* 1. Define your tooltips in a Init function (at the end of this file)
* 2. Call AFTT_Show and AFTT_Hide where needed
* 3. Call your init function in your Startup.d file
*
*
* "API"
* Creates the tooltip
*
*
* int AFTT_Create (var string tt_text, var int background)
* Creates tooltip at the default position (defined by AFTT_posx and AFTT_posy)
* tt_text - text of the tooltip
* background - render background texture? TRUE/FALSE
* return - tooltip handle
*
* int AFTT_CreatePos (var string tt_text, var int x, var int y, var int background)
* Creates tooltip with the top left corner defined by x and y
* tt_text - text of the tooltip
* x - horizontal position (virtual coordinates)
* y - vertical position (virtual coordinates)
* background - render background texture? TRUE/FALSE
* return - tooltip handle
*
* int AFTT_CreatePosPxl (var string tt_text, var int x, var int y, var int background)
* Same as AFTT_CreatePos but with pixel coordinates
*
*
*
*
* int AFTT_CreateCenter (var string tt_text, var int background)
* Creates tooltip in the center of the screen
* tt_text - text of the tooltip
* background - render background texture? TRUE/FALSE
* return - tooltip handle
*
* int AFTT_CreatePosCenter (var string tt_text, var int x, var int y, var int background)
* Creates tooltip with the center defined by x and y
* tt_text - text of the tooltip
* x - horizontal position (virtual coordinates)
* y - vertical position (virtual coordinates)
* background - render background texture? TRUE/FALSE
* return - tooltip handle
*
* int AFTT_CreatePosCenterPxl (var string tt_text, var int x, var int y, var int background)
* Same as AFTT_CreatePosCenter but with pixel coordinates
*
*
*
* Shows the tooltip
* AFTT_Show(var int hndl) - shows the tooltip
* Hides the tooltip
* AFTT_Hide(var int hndl) - hides the tooltip
*
*/
/* TODO:
* [x] fade-in and -out animations
* [x] timed - checks if the tooltip was shown for long enough time
* [x] custom position per tooltip
* [x] background
*/
//========================================
// Getters for positions
//========================================
func int ViewPtr_GetPosX(var int ptr) {
var zCView v; v = _^(ptr);
return v.vposx;
};
func int ViewPtr_GetPosXPxl(var int ptr) {
var zCView v; v = _^(ptr);
return v.pposx;
};
func int View_GetPosX(var int hndl) {
return ViewPtr_GetPosX(getPtr(hndl));
};
func int View_GetPosXPxl(var int hndl) {
return ViewPtr_GetPosXPxl(getPtr(hndl));
};
func int ViewPtr_GetPosY(var int ptr) {
var zCView v; v = _^(ptr);
return v.vposy;
};
func int ViewPtr_GetPosYPxl(var int ptr) {
var zCView v; v = _^(ptr);
return v.pposy;
};
func int View_GetPosY(var int hndl) {
return ViewPtr_GetPosY(getPtr(hndl));
};
func int View_GetPosYPxl(var int hndl) {
return ViewPtr_GetPosYPxl(getPtr(hndl));
};
func void ViewPtr_MoveToX(var int ptr, var int x)
{
ViewPtr_MoveTo(ptr, x, ViewPtr_GetPosY(ptr));
};
func void ViewPtr_MoveToY(var int ptr, var int y)
{
ViewPtr_MoveTo(ptr, ViewPtr_GetPosX(ptr), y);
};
func void View_MoveToX(var int hndl, var int x)
{
ViewPtr_MoveTo(getPtr(hndl), x, ViewPtr_GetPosY(getPtr(hndl)));
};
func void View_MoveToY(var int hndl, var int y)
{
ViewPtr_MoveTo(getPtr(hndl), ViewPtr_GetPosX(getPtr(hndl)), y);
};
//========================================
// Constants
//========================================
const int AFTT_ShowOnce = 0;
const int AFTooltip_seconds = 5;
const int AFTT_posx = 100; // 512 if you are showing FPS
const int AFTT_posy = 100;
const int AFTT_moveAniOffset = 256;
const int AFTT_FadeOutTime = 500;
const string AFTT_font_text = "FONT_OLD_10_WHITE.TGA";
const string AFTT_BgTexture = "INV_BACK_STEAL.TGA";
const string AFTT_nl_separator = "~";
const int AFTT_GlobalHndl = 0;
//========================================
// Tooltip class
//========================================
class AFTooltip
{
var int viewHndl; // view handle
var int shown; // was the tooltip already shown?
var int time; // saves the time, on tooltip show
};
instance AFTooltip@(AFTooltip);
//========================================
// Archiver function
//========================================
func void AFTooltip_Archiver(var AFTooltip this)
{
PM_SaveInt ("viewHndl" , this.viewHndl );
PM_SaveInt ("shown" , this.shown );
// time doesn't have to be saved
};
//========================================
// Unarchiver function
//========================================
func void AFTooltip_Unarchiver(var AFTooltip this)
{
if (PM_Exists("viewHndl" )) { this.viewHndl = PM_Load ("viewHndl" ); };
if (PM_Exists("shown" )) { this.shown = PM_Load ("shown" ); };
};
//========================================
// Deletes the tooltip object
//========================================
func void AFTooltip_Delete(var AFTooltip ttip)
{
if (Hlp_IsValidHandle(ttip.viewHndl))
{
View_DeleteText(ttip.viewHndl);
View_Delete(ttip.viewHndl);
};
};
//========================================
// This function allows me to recalculate
// the width to use in the "sub views"
// so that I can use the same margin value I
// used to calculate the size of the main view
// to position the text-lines
// value - input value
// size - "size" of the space where we are defining the value
// newSize - is the size (in the same axis) of the new view
//========================================
func int ratio(var int value, var int size, var int newSize)
{
Print_GetScreenSize();
var int ratio; ratio = mulf(divf(mkf(size), mkf(newSize)), mkf(value));
return roundf(ratio);
};
//========================================
// Creates an instance of the tooltip object
// tt_text - text of the tooltip
//========================================
func int _AFTT_Create (var string tt_text, var int x, var int y, var int background, var int center)
{
var int tooltip; tooltip = new(AFTooltip@);
var AFTooltip ttip; ttip = get(tooltip);
Print_GetScreenSize();
var int numLines; numLines = STR_SplitCount(tt_text, AFTT_nl_separator);
var string line;
/*
+--------------------------------------------------------------------------+
| . <top margin> . |
|..........................................................................|
| . <text margin top> . |
| . -Font height- . |
| . <text margin bottom> . |
| <left margin> . <text margin top> . <right margin> |
| . -Font height- . |
| . <text margin bottom> . |
|..........................................................................|
| . <bottom margin> . |
+--------------------------------------------------------------------------+
*/
const int leftM = 50;
const int rightM = leftM;
const int topM = 50;
const int bottomM = topM;
const int topTextM = 50;
const int bottomTextM = 50;
var int lineWidth;
var int lineHeight;
// get the longest line
lineWidth = Print_ToVirtual(Print_LongestLineLengthExt(tt_text, AFTT_font_text, AFTT_nl_separator), PS_X);
// get font height
lineHeight = Print_GetFontHeight(AFTT_font_text);
if (!center)
{
ttip.viewHndl = View_Create ( x,
y,
x + lineWidth + leftM + rightM,
y + numLines * (Print_ToVirtual(lineHeight, PS_X) + topTextM + bottomTextM) + bottomM + topM);
}
else
{
ttip.viewHndl = View_CreateCenter ( x,
y,
x + lineWidth + leftM + rightM,
y + numLines * (Print_ToVirtual(lineHeight, PS_X) + topTextM + bottomTextM) + bottomM + topM);
};
if (background)
{
View_SetTexture(ttip.viewHndl, AFTT_BgTexture);
};
View_AddText ( ttip.viewHndl,
ratio(leftM, PS_VMax , lineWidth + leftM + rightM),
ratio(topM, PS_VMax , numLines * (Print_ToVirtual(lineHeight, PS_X) + topTextM + bottomTextM) + bottomM + topM),
tt_text,
AFTT_font_text);
View_SetAlphaAll(ttip.viewHndl, 0);
return tooltip+0;
};
//========================================
// Creates a tooltip - top left corner coordinates
//========================================
func int AFTT_Create (var string tt_text, var int background)
{
return _AFTT_Create(tt_text, AFTT_posx, AFTT_posy, background, 0);
};
func int AFTT_CreatePos (var string tt_text, var int x, var int y, var int background)
{
return _AFTT_Create(tt_text, x, y, background, 0);
};
func int AFTT_CreatePosPxl (var string tt_text, var int x, var int y, var int background)
{
return _AFTT_Create(tt_text, Print_ToVirtual(x, PS_X), Print_ToVirtual(y, PS_Y), background, 0);
};
//========================================
// Creates a tooltip - center coordinates
//========================================
func int AFTT_CreateCenter (var string tt_text, var int background)
{
return _AFTT_Create(tt_text, PS_VMax/2, PS_VMax/2, background, 1);
};
func int AFTT_CreatePosCenter (var string tt_text, var int x, var int y, var int background)
{
return _AFTT_Create(tt_text, x, y, background, 1);
};
func int AFTT_CreatePosCenterPxl (var string tt_text, var int x, var int y, var int background)
{
return _AFTT_Create(tt_text, Print_ToVirtual(x, PS_X), Print_ToVirtual(y, PS_Y), background, 1);
};
//========================================
// Show the tooltip
//========================================
func void AFTT_Show(var int hndl)
{
var AFTooltip ttip; ttip = get(hndl);
// update time
ttip.time = Timer();
PV("ttip.time", ttip.time);
PV("ttip.shown", ttip.shown);
PV("AFTT_ShowOnce", AFTT_ShowOnce);
if (ttip.shown == 1 && AFTT_ShowOnce == 1 ) { return; };
View_Open(ttip.viewHndl);
var int Anim8_motion;
var int Anim8_alpha;
PV("View_GetPosY(ttip.viewHndl)", View_GetPosY(ttip.viewHndl));
Anim8_motion = Anim8_NewExt(View_GetPosY(ttip.viewHndl) + AFTT_moveAniOffset, View_MoveToY, ttip.viewHndl, false);
Anim8(Anim8_motion, View_GetPosY(ttip.viewHndl), AFTT_FadeOutTime, A8_SlowStart);
Anim8_alpha = Anim8_NewExt(0, View_SetAlphaAll, ttip.viewHndl, false);
Anim8(Anim8_alpha, 255, AFTT_FadeOutTime, A8_SlowStart);
Anim8_RemoveIfEmpty (Anim8_motion, true);
Anim8_RemoveIfEmpty (Anim8_alpha, true);
};
func void AFTT_onRemove()
{
View_Close(AFTT_GlobalHndl);
};
//========================================
// Hides the tooltip
//========================================
func void AFTT_Hide(var int hndl)
{
var AFTooltip ttip; ttip = get(hndl);
AFTT_GlobalHndl = ttip.viewHndl;
if (ttip.shown == 1 && AFTT_ShowOnce == 1 ) { return; };
if (AFTT_ShowOnce)
{
var int elapsedTime; elapsedTime = Timer();
if ( (elapsedTime - ttip.time) >= (AFTooltip_seconds * 1000) )
{
ttip.shown = 1;
};
};
var int Anim8_motion;
var int Anim8_alpha;
PV("View_GetPosY(ttip.viewHndl)", View_GetPosY(ttip.viewHndl));
Anim8_motion = Anim8_NewExt(View_GetPosY(ttip.viewHndl), View_MoveToY, ttip.viewHndl, false);
Anim8(Anim8_motion, View_GetPosY(ttip.viewHndl) + AFTT_moveAniOffset, AFTT_FadeOutTime, A8_SlowStart);
Anim8_alpha = Anim8_NewExt(255, View_SetAlphaAll, ttip.viewHndl, false);
Anim8(Anim8_alpha, 0, AFTT_FadeOutTime, A8_SlowStart);
Anim8_CallOnRemove(Anim8_alpha, AFTT_onRemove);
Anim8_RemoveIfEmpty (Anim8_motion, true);
Anim8_RemoveIfEmpty (Anim8_alpha, true);
};
And here is my init function I call after Ikarus and LeGo init functions.
Spoiler:(zum lesen bitte Text markieren)
Code:
const int SLEEP_TT = 0;
func void AFTT_Init()
{
// Loads the setting from an ini file
// AFTT_ShowOnce = STR_ToInt( MEM_GetGothOpt("AF_Tooltips", "ShowOnce"));
const int once = 0;
if (!once) {
// Bed
if (!SLEEP_TT)
{
SLEEP_TT = AFTT_Create("You can use bed to sleep.~Choose the hour you wake up using arrow keys or A/D.~Use PageUp/PageDown or Q/E to choose time in 6 hour increments.", TRUE);
};
// -----> Here you can add more tooltips
once = 1;
}; };
I don't quite understand how exactly PermMem works Not sure if I am supposed to save the PermMem handle like I would save an int, I haven't found anything about this particular use case (saving PM handles) on the wiki.
Handles are simply integers and do not need special consideration (in fact, you don't need archive/unarchive functions at all in your case, because you're only storing "primitive" types, i.e. integers). Your code looks correct, but the crash report shows that you're right - there seems to be a problem during unarchiving. It doesn't even get to your code, though, it's still trying to read the save file. Can you post the corresponding save file? It's the SCRPTSAVE.SAV in the savegame folder, a simple text file.
How are you testing your changes? Did you create a proper mod (with a custom .ini.file)? I think we support all modes by now, but that might be another source of error.
Oh, so primitive types get stored automatically? That's amazing!
Yes, I have my testing mod with its own ini file.
I make changes, I compile the game and then check, if the view shows up and properly closes, then I check the number of handles (if there are no bugs, that might be multiplaying instances) and then save the game and load -> then it crashes (crashes even if I close the game and then try to load it, after launching again).
There are also some other handles, as you may see, but I tested all of them (by turning them off - they are my other featuers I am working on) and only these tooltip ones are crashing.
There are linebreaks in the save file, which really should not be there. You're using ~ as a line seperator (as you should), but that shouldn't insert newlines into the string
I'm currently at work, but maybe I can think about the problem more later. For now you could confirm that it works if you use single-line tooltips (you could simply delete the newlines from the tooltips in the savefile as well).
Yes, that was it! It was my bad, in my local version there was a white character (probably missclick, I made in my text editor) at the end of the string.
Thank you, meybe we could add a string function, that strips strings off white non-printable characters?
But thank you very much, everything works really nice
I have to raise the Milky-way message about talent. With the new version of Lego, some players lose the pointer to the NPC, because of which the stack overflows and crashes.
I have to raise the Milky-way message about talent. With the new version of Lego, some players lose the pointer to the NPC, because of which the stack overflows and crashes.
[f] 03:11 Fault: 0 Q: [start of stacktrace][f] 03:11 Fault: 0 Q: MEMINT_HANDLEERROR(2, 'MEM_PtrToInst: ptr is NULL. Use MEM_NullToInst if that's what you want.') + 62 bytes
[f] 03:11 Fault: 0 Q: MEM_WARN('MEM_PtrToInst: ptr is NULL. Use MEM_NullToInst if that's what you want.') + 21 bytes
[f] 03:11 Fault: 0 Q: MEM_PTRTOINST(706017644) + 77 bytes
[f] 03:11 Fault: 0 Q: MEM_ARRAYWRITE(0, 2, 1) + 28 bytes
[f] 03:11 Fault: 0 Q: TAL_SETVALUE((instance)1717866344, 2, 1) + 133 bytes
[f] 03:11 Fault: 0 Q: B_INITNPCGLOBALS() + 60 bytes
[f] 03:11 Fault: 0 Q: B_ENTER_LOAWORLD() + 5 bytes
[f] 03:11 Fault: 0 Q: B_LOA_KAPITELWECHSEL(2, 1) + 350 bytes
[f] 03:11 Fault: 0 Q: DIA_AM_K2_SQ_EXPEDITION_HAMILKAR_QUEST_KAP2_INFO() + 295 bytes
[f] 03:11 Fault: 0 Q: [end of stacktrace]
[w] 03:11 Warn: 0 Q: MEM_PtrToInst: ptr is NULL. Use MEM_NullToInst if that's what you want.
[i] 03:11 Info: 2 U: PAR: Adressing an empty Instance : ZCARRAY.NUMINARRAY .... <zParser_Symbol.cpp,#365>
Modification of Legend of Ahssun, only Russian translation.
Interestingly, Ninja and any patch that uses Lego fixes this bug.
lego 2.8.0, ikarus 1.2.2
There were many changes and fixes to LeGo and Ikarus since the release of some mods.
Ninja always provides the latest LeGo and Ikarus (if a patch requests both features)
if e.g. LoA uses LeGo 2.4.0 and there were issues with the old code, you would not be able to reproduce it with the current code.
That's why every single request with issues regarding LeGo / Ikarus should always be prefaced with the affected version of each.
I for myself know that there were few issues in LeGo regarding Talents and Buffs, which were fixed with the latest versions.
There were many changes and fixes to LeGo and Ikarus since the release of some mods.
Ninja always provides the latest LeGo and Ikarus (if a patch requests both features)
if e.g. LoA uses LeGo 2.4.0 and there were issues with the old code, you would not be able to reproduce it with the current code.
That's why every single request with issues regarding LeGo / Ikarus should always be prefaced with the affected version of each.
I for myself know that there were few issues in LeGo regarding Talents and Buffs, which were fixed with the latest versions.
I understood that. But the change in the dev version is not as unaffected. The difference between the 2.8.0 and dev versions is in two scripts that do not relate to this problem in any way.
Noticed an oddity when using AI_Function
Sometimes the dialog ends incorrectly, which is why hero still has aivar[AIV_INVINCIBLE] 1
for example, such a dialog. (From the mods Arholos)
If I remember correctly, weird things happen in general when ending a dialog before self has had any ai_output commands - so that might be what's going on here as well? There are some funny workarounds, like an ai_output that is not capitalized because it will then be ignored in some way but it will still make the ending of the dialog work properly.
If you're interested, the bug looks like this. Maybe of course it is possible to write a script better, I have an idea only to start death after n-time
When player is in dialogue with an NPC - NPC has ZS state ZS_Talk.
ZS_Talk_Loop takes care of aivar[AIV_INVINCIBLE] - it is in this loop where variable is set to FALSE for both player (other) and NPC (self) when dialogue is ended.
If you kill NPC before closing dialogue (before InfoManager_HasFinished) then NPC's state will change to ZS_Dead - and this piece of code will not be executed:
Code:
func int ZS_Talk_Loop () {
if (InfoManager_HasFinished())
&& (zsTalkBugfix == TRUE) {
self.aivar[AIV_INVINCIBLE] = FALSE;
other.aivar[AIV_INVINCIBLE] = FALSE;
This is not caused by AI_Function - it is more you breaking dialogue logic by killing NPC before exiting dialogue properly.
Maybe a better solution would be to create frame-function that would be checking whether dialogue has ended and would kill NPC only afterwards.
Ich möchte dem Spieler eine Textur anzeigen. Diese soll sich genau in der Bildschirmmitte befinden. Dafür dürfte sich dann wohl die LeGo-Funktion View_CreateCenter eignen. Aber irgendwie krieg ich das nicht hin. Ich hab jetzt schon so viel mit den Werten probiert, aber die Textur sitzt immer woanders, nur nicht dort, wo ich sie eigentlich haben will. Auch kriege ich es nicht hin, dass sie ihre Originalgröße, 300x300, verwendet. Ich bin mir sicher, dass für letzteres die beiden hinteren Werte verantwortlich sind, aber die ist dennoch immer verzogen. Ich fühle mich gerade ziemlich doof, dass ich so was simples nicht hinbekomme, aber kann mir jemand sagen, wie ich das mache und mir das ggf. auch noch mal etwas genauer erklären, mit diesen ganzen View_Create Werten?
Ist die Dokumentation da an irgendeiner Stelle unklar? Verstehst du, was virtuelle Koordinaten sind? Die Kurzerklärung: Bei virtuellen Koordinaten tut man so, als wäre der Bildschirm des Spielers genau 8192x8192 Pixel groß. Die Mitte des Bildschirms befindet sich dann also immer an den Koordinaten (4096, 4096). Falls du die Größe der Textur nicht skalieren möchtest (das bedeutet aber auch, dass wenn der Spieler eine höhere Auflösung benutzt sie kleiner erscheint), kannst du die wirklichen Pixel in virtuelle Pixel umrechnen mit Print_ToVirtual, also z.B.
Super, danke Lehona. Das hat schon gereicht, ich hab es nun hinbekommen. Das große Problem dabei war tatsächlich, dass ich nicht wusste, was virtuelle Koordinaten sind.
Zitat von Lehona
Ist die Dokumentation da an irgendeiner Stelle unklar? Verstehst du, was virtuelle Koordinaten sind? Die Kurzerklärung: Bei virtuellen Koordinaten tut man so, als wäre der Bildschirm des Spielers genau 8192x8192 Pixel groß. Die Mitte des Bildschirms befindet sich dann also immer an den Koordinaten (4096, 4096). Falls du die Größe der Textur nicht skalieren möchtest (das bedeutet aber auch, dass wenn der Spieler eine höhere Auflösung benutzt sie kleiner erscheint), kannst du die wirklichen Pixel in virtuelle Pixel umrechnen mit Print_ToVirtual, also z.B.
ich benutze für Tagebuch Einträge, XP, Item Übergabe usw. PrintS_Ext. Den Text wurde ich gerne mittig, relativ weit unten ausgeben.
Das Problem was nun kommt ist, das er sich am ersten Buchstaben orientiert und nicht am "mittigsten" Buchstaben im Text, daher kann ich den Text natürlich nie mittig ausrichten, sondern immer nur Linksbündig.
Ansonsten bietet LeGo auch die Möglichkeit an, das selber zu berechnen, mittels Print_GetStringWidth und ein bisschen Zahlengeschubse (die genaue Formel überlasse ich mal dem geneigten Leser ). Falls es mal irgendwo anders als in der Mitte nicht-linksbündig sein soll.
In the modification of Arholos, there is a problem with using item_helper_inst - there this instance can get itself an armor and put it on - which is why the armor model breaks. it is necessary in itm_getptr to prohibit the creation of armor at item_helper_inst
or make sure that this assistant does not equip anything.