| Name | Typ | Von |
|---|---|---|
| Argo Bubbles: dynamische, gestaltbare Sprechblasen | Definition | The Argonauts |
Mit diesem Skript werden die Texte deiner Charaktere zu Sprechblasen.
| Das Skript „Argo Bubbles“ basiert auf dem „Advanced Speechbubble Script“ von Sebastian alias turbomodus. | |
Anleitung
1. Füge das Hauptskript im Skripteditor von Visionaire Studio hinzu und lege es als Definitionsskript fest.
2. Erstelle die Grafiken für die Sprechblasen und lege sie in deinen Projektordner oder einen Unterordner. Einfache Grafiken sind im Demoprojekt enthalten, das du im Abschnitt Ressourcen herunterladen kannst.
3. Passe die Einstellungen oben im Skript an (zwischen "DEFINE YOUR SETTINGS HERE" und "USUALLY NO NEED TO CHANGE ANYTHING BELOW THIS LINE").
Beschreibung
Das Skript ist ausführlich kommentiert und erklärt dir alles, was du für seine Funktionen brauchst. Eine ausführliche Beschreibung der Einstellungen findest du auf dieser Seite.
So entstehen die Argo Bubbles
- Der normale Charaktertext wird nicht angezeigt, sondern intern gespeichert und in die Sprechblase übernommen. Die Schrift bleibt dabei dieselbe, nämlich die Schrift, die im Editor für den jeweiligen Charakter festgelegt ist. Wähle für den Charakter also eine Schrift, die zum Design deiner Sprechblasen passt. Textformatierungen und Texteffekte bleiben erhalten.
- Die Hauptgrafik der Sprechblase wird in neun Teile zerschnitten, die einzeln gestreckt und dann passend zur berechneten Größe wieder zusammengesetzt werden (siehe Grafik unten). Diese „Ninerect“-Technik verhindert verzerrte Ecken, schränkt aber die möglichen Formen ein: Ovale oder ausgefallene Formen sind mit diesem Skript nicht möglich, die Blasen müssen mehr oder weniger rechteckig sein.
- Meist soll ein Zeiger an der Blase auf den sprechenden Charakter weisen. Je nach Ausrichtung und Blickrichtung des Charakters wählt das Skript eine von bis zu vier Zeigergrafiken und platziert sie passend. Zeiger sind aber optional.
- Du kannst der Blase ein zusätzliches Bild hinzufügen. Gedacht ist es für ein Porträt des Charakters, du kannst aber natürlich jedes beliebige Bild nehmen.
Einstellungen
Allgemeine Einstellungen
- enable_shader_viewport_adjustments: Bei true berücksichtigt das Skript Änderungen am Bildausschnitt durch das Shader Toolkit von Visionaire. Nutzt du das Shader Toolkit nicht, kannst du die Option auf false setzen.
Standardstil der Sprechblase
- align_h: Horizontale Ausrichtung der Blase zum Charakter. Mögliche Werte: (center|left|right|char_facing). Mit "char_facing" wird die Blase je nach Blickrichtung des Charakters links oder rechts ausgerichtet.
- align_v: Vertikale Ausrichtung der Blase zum Charakter. Mögliche Werte: (top|bottom). Davon hängt auch die Position des Zeigers ab: Bei oben ausgerichteter Blase sitzt er unten an der Blase und umgekehrt.
- flip_v_align_on_edge: Bei true wird eine oben ausgerichtete Blase automatisch unten ausgerichtet (und umgekehrt), wenn der Charakter dem oberen (unteren) Bildschirmrand zu nahe kommt.
- bubble_offset_x: Horizontaler Versatz der Blase gegenüber der Ausgangsposition mittig über dem Kopf des Charakters. Positive Werte verschieben die Blase in Blickrichtung des Charakters.
- bubble_top_offset_y: Vertikaler Versatz für oben ausgerichtete Blasen. Positive Werte verschieben die Blase nach oben.
- bubble_bottom_offset_y: Vertikaler Versatz für unten ausgerichtete Blasen. Positive Werte verschieben die Blase nach unten.
- adjust_v_offset_to_char_scale: Bei true wird beim vertikalen Versatz die aktuelle Skalierung des Charakters berücksichtigt. Der Versatz (aus "bubble_top_offset_y" bzw. "bubble_bottom_offset_y") wird entsprechend verkleinert oder vergrößert.
- color: Farbtönung der Blase (und des Zeigers). Achtung: Die Farbe wird hexadezimal in BGR-Reihenfolge angegeben (nicht RGB), mit vorangestelltem "0x". Der Standardwert 0xffffff (Weiß) bedeutet keine Tönung.
- text_align_h: Horizontale Textausrichtung in der Blase. Mögliche Werte: (center|left|right). Sichtbar wirkt sich das nur bei mehrzeiligem Text und/oder fester Blasenbreite aus.
- text_align_v: Vertikale Textausrichtung in der Blase. Mögliche Werte: (center|top|bottom). Sichtbar wirkt sich das nur bei fester Blasenhöhe aus.
- padding: Abstand zwischen Textblock und Außenrand der Blase, als Tabelle {"top", "right", "bottom", "left"}.
- fixed_width: Feste Breite der Blase; 0 bedeutet keine feste Breite. Beachte, dass die Breite derzeit keinen Einfluss auf Zeilenumbrüche hat.
- fixed_height: Feste Höhe der Blase; 0 bedeutet keine feste Höhe.
- fixed_pos: Feste Bildschirmposition für die Blase, als Tabelle {"x", "y"}. Sie ersetzt die Position des Charakters als Ausgangspunkt der Berechnung; wo die Blase landet, hängt also von deinen Ausrichtungseinstellungen ab. Setze "align_h" bei fester Position nicht auf "char_facing", sonst wechselt die Blase je nach Blickrichtung des Charakters die Position.
- expand_fixed_dims: Bei true wächst eine Blase mit fester Breite oder Höhe mit, wenn der Text nicht hineinpasst. Die feste Breite bzw. Höhe wirkt dann wie eine Mindestbreite bzw. Mindesthöhe.
- file_bubble: Pfad zur Hauptgrafik der Blase. Die Grafik muss für die „Ninerect“-Technik geeignet sein. Der Pfad ist relativ zum Projektordner und beginnt mit "vispath:".
- ninerect: Maße zum Zerschneiden der Blasengrafik, als Tabelle {"x", "y", "width, "height"}. x und y legen Breite und Höhe der Kachel oben links fest, width und height die Breite und Höhe der mittleren Kachel (siehe Grafik unten).
- file_portrait: Pfad zu einem Porträtbild. Der Pfad ist relativ zum Projektordner und beginnt mit "vispath:". Setze den Wert auf "" (leerer String) für eine Blase ohne Porträt.
- portrait_align_h: Horizontale Ausrichtung des Porträts in der Blase. Mögliche Werte: (left|right). Zwischen Porträt und Blasenrand gibt es keinen Innenabstand, padding gilt nur für den Text. Zum Verschieben füge deinem Porträt transparente Bereiche hinzu (siehe Grafik unten).
- portrait_align_v: Vertikale Ausrichtung des Porträts in der Blase. Mögliche Werte: (top|center|bottom). Zwischen Porträt und Blasenrand gibt es keinen Innenabstand, padding gilt nur für den Text. Zum Verschieben füge deinem Porträt transparente Bereiche hinzu (siehe Grafik unten).
- file_pointer_bottom_right: Pfad zur Grafik des Zeigers nach rechts unten. Der Pfad ist relativ zum Projektordner und beginnt mit "vispath:". Setze den Wert auf "" (leerer String) für eine Blase ohne Zeiger.
- file_pointer_bottom_left: Pfad zur Grafik des Zeigers nach links unten. Der Pfad ist relativ zum Projektordner und beginnt mit "vispath:". Setze den Wert auf "" (leerer String) für eine Blase ohne Zeiger.
- pointer_bottom_right_offset_x: Horizontaler Versatz des Zeigers nach rechts unten. Positive Werte verschieben den Zeiger in Blickrichtung des Charakters.
- pointer_bottom_left_offset_x: Horizontaler Versatz des Zeigers nach links unten. Positive Werte verschieben den Zeiger in Blickrichtung des Charakters.
- pointer_bottom_offset_y: Vertikaler Versatz der Zeiger nach unten. Positive Werte verschieben den Zeiger nach innen.
- file_pointer_top_right: Pfad zur Grafik des Zeigers nach rechts oben. Der Pfad ist relativ zum Projektordner und beginnt mit "vispath:". Setze den Wert auf "" (leerer String) für eine Blase ohne Zeiger.
- file_pointer_top_left: Pfad zur Grafik des Zeigers nach links oben. Der Pfad ist relativ zum Projektordner und beginnt mit "vispath:". Setze den Wert auf "" (leerer String) für eine Blase ohne Zeiger.
- pointer_top_right_offset_x: Horizontaler Versatz des Zeigers nach rechts oben. Positive Werte verschieben den Zeiger in Blickrichtung des Charakters.
- pointer_top_left_offset_x: Horizontaler Versatz des Zeigers nach links oben. Positive Werte verschieben den Zeiger in Blickrichtung des Charakters.
- pointer_top_offset_y: Vertikaler Versatz der Zeiger nach oben. Positive Werte verschieben den Zeiger nach innen.
- pointer_min_distance: Mindestabstand des Zeigers zum Rand der Blase (siehe Grafik unten).
- min_distance: Mindestabstand der Blase zum Bildschirmrand.
Eigene Blasenstile
Eine Hauptfunktion von „Argo Bubbles“ sind mehrere Blasenstile. Du kannst zum Beispiel jedem Charakter eine eigene Farbe oder ein eigenes Design geben. Oder ein NPC braucht wegen seines Standorts eine andere Position der Blase als üblich. Du kannst sogar mehrere Stile für denselben Charakter festlegen und im Spiel zwischen ihnen wechseln.
Der Standardstil steht in der Tabelle "default_style". So fügst du weitere Stile hinzu:
- Trage eigene Stile in die Tabelle custom_styles ein. Du kannst alle Eigenschaften des Standardstils überschreiben; nicht angegebene Eigenschaften übernehmen die Standardwerte.
- Jeder eigene Stil gilt für einen bestimmten Charakter. Trage dessen Namen in die zusätzliche Eigenschaft char ein.
-- Example
local custom_styles = {
{
char = "Hero",
align_h = "left",
align_v = "bottom",
bubble_offset_x = 40,
bubble_top_offset_y = 35,
file_pointer_top_right = "vispath:gui/bubble_pointer_tr.png",
pointer_top_offset_y = 10
},
{
char = "Villain",
file_bubble = "vispath:gui/bubble_black.png"
},
{
-- next style definition here ...
}
}
- Nutzt ein Charakter im ganzen Spiel nur einen Stil (den Standardstil oder einen eigenen), musst du sonst nichts tun.
- Soll der Charakter im Spiel den Stil wechseln, gib ihm einen Visionaire-Wert mit dem Namen "argo_bubble". Über diesen Wert wählst du den aktiven Stil: 0 ist der Standardstil, 1 der erste eigene Stil dieses Charakters, 2 der zweite usw. Du kannst pro Charakter beliebig viele eigene Stile anlegen, trage sie einfach in die Tabelle "custom_styles" ein.
Erweiterte Einstellungen
- ab_bind_to_handler: Den Event-Handler "textStopped" kannst du in deinem Projekt nur einmal festlegen. Willst du das in einem anderen Skript tun, setze diese Variable auf false. Dann musst du in jenem Skript "destroy_argo_bubble(text)" aufrufen.
- ab_on_text_stopped(): Den Event-Handler "textStopped" kannst du in deinem Projekt nur einmal festlegen. Legst du ihn hier im Skript Argo Bubbles fest und willst aus diesem Handler weitere Funktionen aufrufen, füge die Aufrufe in diese Funktion ein. Gilt nur, wenn "ab_bind_to_handler" auf true steht.
Blasen entfernen
Mit der Funktion kill_argo_bubbles() entfernst du sofort alle aktuellen Blasen vom Bildschirm. Das brauchst du zum Beispiel, wenn der Spieler das Menü per Taste öffnen kann, denn das geht auch, während ein Charakter spricht und eine Sprechblase zu sehen ist. Mit der Kill-Funktion verhinderst du, dass die Blase im Menü auftaucht. Ein Szenenwechsel blendet eine Blase nicht automatisch aus.
Tutorial
Wenn du genauer verstehen willst, wie die Sprechblasen entstehen und den Text von Visionaire ersetzen, sieh dir das Tutorial an; es liegt in der ZIP-Datei. Wenn du das Skript nur in deinem Projekt verwenden willst, musst du es nicht lesen.
Beachte, dass das Tutorial für Version 2.1.2 des Skripts geschrieben wurde. Die Grundidee ist dieselbe, der Code hat sich seitdem aber stark verändert.
Hauptskript
--[[
Argo Bubbles
------------
This script turns character texts into speech bubbles. It allows defining differently styled bubbles for each character, or even different speech bubble styles for the same character to switch between during the game.
Authors: The Argonauts
(with contributions by Mulewa and some lines of code
taken from the Visionaire Shader Toolkit by Simon Scheckel)
Version: 2.3.1
Date: 2026-06-01
Requirements: Visionaire Studio 5.3 or higher
License: MIT License (details at the bottom of the script)
Based on: Advanced Speechbubble Script [v1.1] (10/13/2018), written by Sebastian/TURBOMODUS (with permission)
Instructions: https://wiki.visionaire-tracker.net/wiki/Argo_Bubbles
Argonauts games: https://the-argonauts.itch.io/
]]
-------------------------------
-- DEFINE YOUR SETTINGS HERE --
-------------------------------
-- Detailed description of the settings:
-- https://wiki.visionaire-tracker.net/wiki/Argo_Bubbles#Settings
-- or in the "readme.txt" file coming with this script
-- GENERAL SETTINGS
local enable_shader_viewport_adjustments = true
-- DEFAULT BUBBLE STYLE
local default_style = {
align_h = "center",
align_v = "top",
flip_v_align_on_edge = true,
bubble_offset_x = 0,
bubble_top_offset_y = 25,
bubble_bottom_offset_y = 70,
adjust_v_offset_to_char_scale = true,
color = 0xffffff,
text_align_h = "center",
text_align_v = "center",
padding = { top = 15, right = 20, bottom = 12, left = 20 },
fixed_width = 0,
fixed_height = 0,
fixed_pos = false,
expand_fixed_dims = true,
file_bubble = "vispath:gui/bubble.png",
ninerect = { x = 20, y = 15, width = 30, height = 20 },
file_portrait = "",
portrait_align_h = "left",
portrait_align_v = "top",
file_pointer_bottom_right = "vispath:gui/bubble_pointer_br.png",
file_pointer_bottom_left = "vispath:gui/bubble_pointer_bl.png",
pointer_bottom_right_offset_x = 20,
pointer_bottom_left_offset_x = 0,
pointer_bottom_offset_y = 3,
file_pointer_top_right = "",
file_pointer_top_left = "",
pointer_top_right_offset_x = 0,
pointer_top_left_offset_x = 0,
pointer_top_offset_y = 0,
pointer_min_distance = 15,
min_distance = 15
}
-- CUSTOM BUBBLE STYLES
local custom_styles = {}
-- ADVANCED SETTINGS
local ab_bind_to_handler = true
function ab_on_text_stopped(text)
-- add your functions here
end
--------------------------------------------------------
-- USUALLY NO NEED TO CHANGE ANYTHING BELOW THIS LINE --
--------------------------------------------------------
-- Build new custom styles table "c_styles" and fill up with default values
local c_styles = {}
if custom_styles ~= nil then
for key, styles in pairs(custom_styles) do
local num_char_styles = 1
if c_styles[ styles["char"] ] ~= nil then
num_char_styles = #c_styles[ styles["char"] ] + 1
c_styles[ styles["char"] ][num_char_styles] = {}
else
c_styles[ styles["char"] ] = {{}}
end
for k, v in pairs(default_style) do
c_styles[ styles["char"] ][num_char_styles][k] = v
end
for k, v in pairs(styles) do
c_styles[ styles["char"] ][num_char_styles][k] = v
end
end
end
-- CALL FUNCTIONS
local bubbles = {}
local killed = {}
-- New mode: create bubbles in "textRender" function
function show_argo_bubble(text)
if text.Owner.tableId == eCharacters then
-- Add current character text to the bubbles table, if not already there (and bubbles not killed early)
if bubbles[text.id] == nil and killed[text.id] == nil then
bubbles[text.id] = {
Text = text.CurrentText,
Owner = text.Owner.name,
Background = text.Background,
Object = text
}
end
-- override the default text rendering
return true
end
return false
end
function destroy_argo_bubble(text)
-- Remove current text from bubbles table
bubbles[text.id] = nil
-- Call external functions that use the "textStopped" event handler
if ab_on_text_stopped ~= nil then
ab_on_text_stopped(text)
end
end
-- Call this function to immediately destroy all bubbles
function kill_argo_bubbles()
-- Since we use the render hook, the bubbles table gets quickly filled again;
-- so we store the ids of texts to be killed in a separate table
for key, val in pairs(bubbles) do
killed[key] = key
end
bubbles = {}
end
-- Register hook function and bind to event handler
registerHookFunction("textRender", "show_argo_bubble")
if ab_bind_to_handler then
registerEventHandler("textStopped","destroy_argo_bubble")
end
-- DRAW FUNCTIONS
-- Draw background texts from bubbles table below interfaces (and cursors)
function bubble_below_interface()
for key, val in pairs(bubbles) do
if val.Background then
create_bubble(key, val)
end
end
end
-- Draw non-background texts from bubbles table above interfaces
function bubble_above_interface()
for key, val in pairs(bubbles) do
if not val.Background then
create_bubble(key, val)
end
end
end
-- Main bubble function
function create_bubble(key, val)
-- Get talking character
local char = Characters[val.Owner]
local pos = graphics.getCharacterTextPosition(char)
-- Get bubble style
local bubble_style = get_style(key, val, char)
-- Get shader toolkit viewport adjustments
local shader_adjusted_viewport = get_viewport_adjustments(bubble_style)
-- Facing direction (if bubble alignment is set to "left" or "right", the actual facing direction is irrelevant)
local char_facing = "right"
if bubble_style.align_h == "left" then
char_facing = "left"
elseif bubble_style.align_h ~= "right" then
if char.Direction > 90 and char.Direction < 270 then
char_facing = "left"
end
end
-- Get text and bubble dimensions
local text_and_bubble_dims = get_text_and_dims(key, val, char, bubble_style)
local text_dim = text_and_bubble_dims.text_dim
local bubble_dim = text_and_bubble_dims.bubble_dim
-- Get the bubble position
pos = get_bubble_position(pos, bubble_style, bubble_dim, char, char_facing, shader_adjusted_viewport)
-- Get bubble graphic and define ninerect geometry
local bubble_sprite
local dest_rect = { x = pos.x, y = pos.y, width = bubble_dim.x, height = bubble_dim.y }
-- Take sprite from bubbles table, if cached
if val.bubble_sprite ~= nil then
bubble_sprite = val.bubble_sprite
else
bubble_sprite = graphics.loadFromFile(bubble_style.file_bubble)
-- Cache bubble sprite
bubbles[key].bubble_sprite = bubble_sprite
end
-- Draw the bubble
graphics.drawSpriteWithNineRect(bubble_sprite, dest_rect, bubble_style.ninerect, bubble_style.color, 1.0)
-- Get portrait graphic
local portrait_sprite = nil
-- Take sprite from bubbles table, if cached
if val.portrait_sprite ~= nil then
portrait_sprite = val.portrait_sprite
elseif bubble_style.file_portrait ~= nil and bubble_style.file_portrait ~= "" then
portrait_sprite = graphics.loadFromFile(bubble_style.file_portrait)
-- Cache bubble sprite
bubbles[key].portrait_sprite = portrait_sprite
end
-- Continue with portrait, if graphic has been defined
if portrait_sprite ~= nil then
-- Get position of portrait image
portrait_sprite.position = get_portrait_position(pos, bubble_style, bubble_dim, portrait_sprite)
-- Draw the portrait
graphics.drawSprite(portrait_sprite, 1.0)
end
-- Get pointer graphic
local pointer_sprite = nil
local pointer_offset = 0
if char_facing == "left" then
-- right pointer when char facing left
if (bubble_style.align_v_temp ~= nil and bubble_style.align_v_temp == "bottom") or (bubble_style.align_v_temp == nil and bubble_style.align_v == "bottom") then
-- top pointer when bubble aligned to bottom
if bubble_style.file_pointer_top_right ~= nil and bubble_style.file_pointer_top_right ~= "" then
pointer_sprite = graphics.loadFromFile(bubble_style.file_pointer_top_right)
pointer_offset = -bubble_style.pointer_top_right_offset_x
end
else
-- bottom pointer when bubble aligned to top
if bubble_style.file_pointer_bottom_right ~= nil and bubble_style.file_pointer_bottom_right ~= "" then
pointer_sprite = graphics.loadFromFile(bubble_style.file_pointer_bottom_right)
pointer_offset = -bubble_style.pointer_bottom_right_offset_x
end
end
else
-- left pointer when char facing right
if (bubble_style.align_v_temp ~= nil and bubble_style.align_v_temp == "bottom") or (bubble_style.align_v_temp == nil and bubble_style.align_v == "bottom") then
-- top pointer when bubble aligned to bottom
if bubble_style.file_pointer_top_left ~= nil and bubble_style.file_pointer_top_left ~= "" then
pointer_sprite = graphics.loadFromFile(bubble_style.file_pointer_top_left)
pointer_offset = bubble_style.pointer_top_left_offset_x
end
else
-- bottom pointer when bubble aligned to top
if bubble_style.file_pointer_bottom_left ~= nil and bubble_style.file_pointer_bottom_left ~= "" then
pointer_sprite = graphics.loadFromFile(bubble_style.file_pointer_bottom_left)
pointer_offset = bubble_style.pointer_bottom_left_offset_x
end
end
end
-- Continue with pointer, if graphic has been defined
if pointer_sprite ~= nil then
-- Get position of pointer image
pointer_sprite.position = get_pointer_position(pos, bubble_style, bubble_dim, pointer_offset, char, char_facing, shader_adjusted_viewport, pointer_sprite)
-- Draw the pointer
graphics.drawSprite(pointer_sprite, 1.0, bubble_style.color)
end
-- Draw the text
-- Calculate vertical alignment of text inside bubble (only if fixed height and not at the top)
local text_box_pos_y = pos.y + bubble_style.padding.top
if bubble_style.fixed_height ~= nil then
if bubble_style.text_align_v == "center" then
text_box_pos_y = pos.y + bubble_style.padding.top + (bubble_dim.y - bubble_style.padding.top - bubble_style.padding.bottom - text_dim.y) / 2
elseif bubble_style.text_align_v == "bottom" then
text_box_pos_y = pos.y + bubble_dim.y - bubble_style.padding.bottom - text_dim.y
end
end
-- Draw the text object
local text_pos = {x = 0, y = text_box_pos_y}
local alignment = 2
if bubble_style.text_align_h == "left" then
text_pos.x = pos.x + bubble_style.padding.left
alignment = 0
elseif bubble_style.text_align_h == "right" then
text_pos.x = pos.x + bubble_dim.x - bubble_style.padding.right
alignment = 1
else -- center
text_pos.x = pos.x + bubble_style.padding.left + (bubble_dim.x - bubble_style.padding.left - bubble_style.padding.right) / 2
end
graphics.drawTextObject(val.Object, text_pos.x, text_pos.y, alignment)
end
-- Get style for the current bubble
function get_style(key, val, char)
local bubble_style = default_style
-- Take style from bubbles table, if cached
if val.style ~= nil then
bubble_style = val.style
else
if c_styles ~= nil and c_styles[char.name] ~= nil then
if Characters[char.name].Values["argo_bubble"] ~= nil then
if c_styles[char.name][ Characters[char.name].Values["argo_bubble"].Int ] ~= nil then
bubble_style = c_styles[char.name][ Characters[char.name].Values["argo_bubble"].Int ]
end
else
bubble_style = c_styles[char.name][1]
end
end
-- Cache style
bubbles[key].style = bubble_style
end
return bubble_style
end
-- Get shader toolkit viewport adjustments (probably not fully supported yet)
function get_viewport_adjustments(bubble_style)
local shader_adjusted_viewport = {x = 0, y = 0, scale = 1}
if enable_shader_viewport_adjustments and shader_newViewport ~= nil and bubble_style.fixed_pos == false then
if shader_viewportTransition > 0.0 and shader_viewportTransition < 1.0 then
shader_adjusted_viewport.scale = (1.0 - shader_viewportTransition) * shader_oldViewport.scale + shader_viewportTransition * shader_newViewport.scale
local startCenter = {
x = shader_oldViewport.x + c_res.x / shader_oldViewport.scale * shader_interpolationPoint.x,
y = shader_oldViewport.y + c_res.y / shader_oldViewport.scale * shader_interpolationPoint.y
}
local endCenter = {
x = shader_newViewport.x + c_res.x / shader_newViewport.scale * shader_interpolationPoint.x,
y = shader_newViewport.y + c_res.y / shader_newViewport.scale * shader_interpolationPoint.y
}
local interpolatedCenter = {
x = startCenter.x * (1.0 - shader_viewportTransition) + endCenter.x * shader_viewportTransition,
y = startCenter.y * (1.0 - shader_viewportTransition) + endCenter.y * shader_viewportTransition
}
shader_adjusted_viewport.x = interpolatedCenter.x - c_res.x / shader_scale * shader_interpolationPoint.x
shader_adjusted_viewport.y = interpolatedCenter.y - c_res.y / shader_scale * shader_interpolationPoint.y
else
shader_adjusted_viewport = shader_newViewport
end
end
return shader_adjusted_viewport
end
-- Calculate text and bubble dimensions
function get_text_and_dims(key, val, char, bubble_style)
local txt = ""
local lines = {}
local text_dim = {x = 0, y = 0}
local bubble_dim = {x = 0, y = 0}
graphics.font = char.Font
-- Take from bubbles table, if cached
if val.text_dim ~= nil then
txt = val.txt
lines = val.lines
text_dim = val.text_dim
bubble_dim = val.bubble_dim
else
txt = val.Text:gsub("<br/"..">", "\n")
lines = graphics.performLinebreaks(txt)
for k, line in ipairs(lines) do
local tempdim = graphics.fontDimension(line)
if text_dim.x < tempdim.x then
text_dim.x = tempdim.x
end
end
-- TrueType Font
local letter_h = char.Font.Size
-- Bitmap Font
if not char.Font.TrueTypeFont then
letter_h = char.Font.Letters[1].height
end
text_dim.y = #lines * (letter_h + char.Font.VerticalLetterSpacing) - char.Font.VerticalLetterSpacing
bubble_dim.x = text_dim.x + bubble_style.padding.right + bubble_style.padding.left
bubble_dim.y = text_dim.y + bubble_style.padding.top + bubble_style.padding.bottom
if bubble_style.fixed_width > 0 then
if not bubble_style.expand_fixed_dims or bubble_style.fixed_width > bubble_dim.x then
bubble_dim.x = bubble_style.fixed_width
end
end
if bubble_style.fixed_height > 0 then
if not bubble_style.expand_fixed_dims or bubble_style.fixed_height > bubble_dim.y then
bubble_dim.y = bubble_style.fixed_height
end
end
-- Cache dimensions
bubbles[key].txt = txt
bubbles[key].lines = lines
bubbles[key].text_dim = text_dim
bubbles[key].bubble_dim = bubble_dim
end
return { lines = lines, text_dim = text_dim, bubble_dim = bubble_dim }
end
-- Calculate the bubble position
function get_bubble_position(pos, bubble_style, bubble_dim, char, char_facing, shader_adjusted_viewport)
-- Change reference point for bubble with fixed position
if bubble_style.fixed_pos ~= false then
pos.x = bubble_style.fixed_pos.x + game.ScrollPosition.x
pos.y = bubble_style.fixed_pos.y + game.ScrollPosition.y
end
-- X position
if bubble_style.align_h == "right" or (bubble_style.align_h == "char_facing" and char_facing == "right") then
pos.x = (pos.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale
elseif bubble_style.align_h == "left" or (bubble_style.align_h == "char_facing" and char_facing == "left") then
pos.x = (pos.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale - bubble_dim.x
else -- center
pos.x = (pos.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale - bubble_dim.x / 2
end
if char_facing == "left" then
pos.x = pos.x - bubble_style.bubble_offset_x
else -- right
pos.x = pos.x + bubble_style.bubble_offset_x
end
if pos.x < bubble_style.min_distance then
pos.x = bubble_style.min_distance
elseif pos.x > game.WindowResolution.x - bubble_dim.x - bubble_style.min_distance then
pos.x = game.WindowResolution.x - bubble_dim.x - bubble_style.min_distance
end
-- Y position
local temp_y = 0
local char_scale = 1
if bubble_style.adjust_v_offset_to_char_scale then
char_scale = (char.ScaleFactor / 100) * shader_adjusted_viewport.scale
if char.Scale then
char_scale = char_scale * (char.Size / 100)
end
end
if bubble_style.align_v == "bottom" then
temp_y = (pos.y - game.ScrollPosition.y - shader_adjusted_viewport.y) * shader_adjusted_viewport.scale + math.floor(bubble_style.bubble_bottom_offset_y * char_scale)
else -- top
temp_y = (pos.y - game.ScrollPosition.y - shader_adjusted_viewport.y) * shader_adjusted_viewport.scale - bubble_dim.y - math.floor(bubble_style.bubble_top_offset_y * char_scale)
end
if temp_y < bubble_style.min_distance then
if bubble_style.flip_v_align_on_edge and bubble_style.fixed_pos == false then
bubble_style.align_v_temp = "bottom"
pos.y = (pos.y - game.ScrollPosition.y - shader_adjusted_viewport.y) * shader_adjusted_viewport.scale + math.floor(bubble_style.bubble_bottom_offset_y * char_scale)
else
pos.y = bubble_style.min_distance
end
elseif temp_y > game.WindowResolution.y - bubble_dim.y - bubble_style.min_distance then
if bubble_style.flip_v_align_on_edge and bubble_style.fixed_pos == false then
bubble_style.align_v_temp = "top"
pos.y = (pos.y - game.ScrollPosition.y - shader_adjusted_viewport.y) * shader_adjusted_viewport.scale - bubble_dim.y - math.floor(bubble_style.bubble_top_offset_y * char_scale)
else
pos.y = game.WindowResolution.y - bubble_dim.y - bubble_style.min_distance
end
else
pos.y = temp_y
bubble_style.align_v_temp = nil
end
return pos
end
-- Calculate position of portrait image
function get_portrait_position(pos, bubble_style, bubble_dim, portrait_sprite)
local portrait_pos = { x = pos.x, y = pos.y }
if bubble_style.portrait_align_h == "right" then
portrait_pos.x = pos.x + bubble_dim.x - portrait_sprite.width
end
if bubble_style.portrait_align_v == "center" then
portrait_pos.y = pos.y + (bubble_dim.y - portrait_sprite.height) / 2
elseif bubble_style.portrait_align_v == "bottom" then
portrait_pos.y = pos.y + bubble_dim.y - portrait_sprite.height
end
return portrait_pos
end
-- Calculate position of pointer image
function get_pointer_position(pos, bubble_style, bubble_dim, pointer_offset, char, char_facing, shader_adjusted_viewport, pointer_sprite)
-- Calculate pointer position
local pointer_pos = {x = 0, y = 0}
if bubble_style.align_h == "right" or (bubble_style.align_h == "char_facing" and char_facing == "right") then
-- if bubble is aligned right, pointer is positioned leftmost
pointer_pos.x = pos.x + pointer_offset
-- Adjust position, if bubble is too close to the edge
if bubble_style.fixed_pos == false then
if pointer_pos.x < (char.Position.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale + pointer_offset then
pointer_pos.x = (char.Position.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale + pointer_offset
end
end
elseif bubble_style.align_h == "left" or (bubble_style.align_h == "char_facing" and char_facing == "left") then
-- if bubble is aligned left, pointer is positioned rightmost
pointer_pos.x = pos.x + bubble_dim.x - pointer_sprite.width + pointer_offset
-- Adjust position, if bubble is too close to the edge
if bubble_style.fixed_pos == false then
if pointer_pos.x > (char.Position.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale + pointer_offset then
pointer_pos.x = (char.Position.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale + pointer_offset
end
end
else -- center
-- if bubble is aligned center, pointer is positioned at character
if bubble_style.fixed_pos == false then
pointer_pos.x = (char.Position.x - game.ScrollPosition.x - shader_adjusted_viewport.x) * shader_adjusted_viewport.scale + pointer_offset
else
pointer_pos.x = pos.x + bubble_dim.x / 2
end
end
-- Adjust position, if character is too close to the edge
if bubble_style.fixed_pos == false then
if pointer_pos.x < pos.x - pointer_offset then
pointer_pos.x = pos.x - pointer_offset
elseif pointer_pos.x > pos.x + bubble_dim.x - pointer_sprite.width - pointer_offset then
pointer_pos.x = pos.x + bubble_dim.x - pointer_sprite.width - pointer_offset
end
if pointer_pos.x < pos.x + bubble_style.pointer_min_distance then
pointer_pos.x = pos.x + bubble_style.pointer_min_distance
elseif pointer_pos.x > pos.x + bubble_dim.x - pointer_sprite.width - bubble_style.pointer_min_distance then
pointer_pos.x = pos.x + bubble_dim.x - pointer_sprite.width - bubble_style.pointer_min_distance
end
end
if (bubble_style.align_v_temp ~= nil and bubble_style.align_v_temp == "bottom") or (bubble_style.align_v_temp == nil and bubble_style.align_v == "bottom") then
-- pointer on top when bubble aligned to bottom
pointer_pos.y = pos.y - pointer_sprite.height + bubble_style.pointer_top_offset_y
else
-- pointer on bottom when bubble aligned to top
pointer_pos.y = pos.y + bubble_dim.y - bubble_style.pointer_bottom_offset_y
end
return pointer_pos
end
-- ADD DRAW FUNCTIONS TO THE RENDERING
graphics.addDrawFunc("bubble_below_interface()", 0)
graphics.addDrawFunc("bubble_above_interface()", 1)
--[[
MIT License
Copyright 2024 The Argonauts
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
Ressourcen
| Name | Beschreibung |
|---|---|
| argo_bubbles_2.3.1.zip | Ein funktionierendes Beispiel für das Skript. Für die enthaltenen .ved-Dateien brauchst du Visionaire Studio 5.3+. |


