forked from Mirrors/openclonk
396 lines
10 KiB
C
396 lines
10 KiB
C
/**
|
|
ControllerInventoryBar
|
|
|
|
Displays inventory slots and extra information.
|
|
|
|
@authors Zapper, Clonkonaut
|
|
*/
|
|
|
|
/*
|
|
inventory_slot contains an array of proplists with the following attributes:
|
|
ID: submenu ID. Unique in combination with the target == this
|
|
obj: last object that was shown here
|
|
hand: bool, whether select with a hand
|
|
quick: bool, whether this is the quick switch slot
|
|
*/
|
|
|
|
// HUD margin and size in tenths of em.
|
|
static const GUI_Controller_InventoryBar_IconMarginScreenTop = 5;
|
|
static const GUI_Controller_InventoryBar_IconSize = 20;
|
|
static const GUI_Controller_InventoryBar_IconMargin = 5;
|
|
|
|
local inventory_slots;
|
|
local inventory_gui_menu;
|
|
local inventory_gui_id;
|
|
|
|
/* GUI creation */
|
|
|
|
// For custom HUD graphics overload the following function as deemed fit.
|
|
|
|
func AssembleInventoryButton(int max_slots, int slot_number, proplist slot_info)
|
|
{
|
|
// The gui already exists, only update it with a new submenu
|
|
var pos = CalculateButtonPosition(slot_number, max_slots);
|
|
|
|
return
|
|
{
|
|
Target = this,
|
|
slot_number =
|
|
{
|
|
Priority = 3, // Make sure the slot number is drawn above the icon.
|
|
Style = GUI_TextTop,
|
|
Text = Format("%2d", slot_info.slot + 1)
|
|
},
|
|
quick_switch = // Shows quick switch control key if this is the quick switch slot
|
|
{
|
|
Priority = 3,
|
|
Style = GUI_NoCrop | GUI_TextHCenter | GUI_TextBottom,
|
|
Left = "-50%",
|
|
Right = "150%",
|
|
Top = Format(" %s%s", "20%", ToEmString(-2)),
|
|
Bottom = "20%",
|
|
Text = { Std = "", Quick = Format("<c dddd00>[%s]</c>", GetPlayerControlAssignment(GetOwner(), CON_QuickSwitch, true)), Selected = "" }
|
|
},
|
|
Style = GUI_NoCrop,
|
|
ID = slot_info.ID,
|
|
Symbol = {Std = Icon_Menu_Circle, Quick = Icon_Menu_Circle, Selected = Icon_Menu_CircleHighlight},
|
|
Left = pos.Left, Top = pos.Top, Right = pos.Right, Bottom = pos.Bottom,
|
|
count =
|
|
{
|
|
ID = 1000 + slot_info.ID,
|
|
Style = GUI_TextRight | GUI_TextBottom,
|
|
Text = nil,
|
|
Priority = 2
|
|
},
|
|
// Prepare (invisible) extra-slot display circle.
|
|
extra_slot =
|
|
{
|
|
Top = ToEmString(GUI_Controller_InventoryBar_IconSize),
|
|
Bottom = ToEmString(GUI_Controller_InventoryBar_IconSize + GUI_Controller_InventoryBar_IconSize/2),
|
|
Style = GUI_TextLeft,
|
|
Text = nil,
|
|
symbol =// used to display an infinity sign if necessary (Icon_Number)
|
|
{
|
|
Right = ToEmString(GUI_Controller_InventoryBar_IconSize/2),
|
|
GraphicsName = "Inf",
|
|
},
|
|
circle =// shows the item in the extra slot
|
|
{
|
|
Left = ToEmString(GUI_Controller_InventoryBar_IconSize/2),
|
|
Symbol = nil,
|
|
symbol = {}
|
|
}
|
|
},
|
|
overlay = // Custom inventory overlays can be shown here.
|
|
{
|
|
ID = 2000 + slot_info.ID
|
|
}
|
|
};
|
|
}
|
|
|
|
/* Creation / Destruction */
|
|
|
|
private func Construction()
|
|
{
|
|
inventory_slots = [];
|
|
|
|
inventory_gui_menu =
|
|
{
|
|
Target = this,
|
|
Player = NO_OWNER, // will be shown once a gui update occurs
|
|
Style = GUI_Multiple | GUI_IgnoreMouse | GUI_NoCrop
|
|
};
|
|
inventory_gui_id = GuiOpen(inventory_gui_menu);
|
|
|
|
return _inherited(...);
|
|
}
|
|
|
|
private func Destruction()
|
|
{
|
|
GuiClose(inventory_gui_id);
|
|
|
|
_inherited(...);
|
|
}
|
|
|
|
/* Callbacks */
|
|
|
|
public func OnCrewDisabled(object clonk)
|
|
{
|
|
ScheduleUpdateInventory();
|
|
|
|
return _inherited(clonk, ...);
|
|
}
|
|
|
|
public func OnCrewEnabled(object clonk)
|
|
{
|
|
ScheduleUpdateInventory();
|
|
|
|
return _inherited(clonk, ...);
|
|
}
|
|
|
|
public func OnCrewSelection(object clonk, bool deselect)
|
|
{
|
|
ScheduleUpdateInventory();
|
|
|
|
return _inherited(clonk, deselect, ...);
|
|
}
|
|
|
|
// call from HUDAdapter (Clonk)
|
|
public func OnSlotObjectChanged(int slot)
|
|
{
|
|
// refresh inventory
|
|
ScheduleUpdateInventory();
|
|
|
|
return _inherited(slot, ...);
|
|
}
|
|
|
|
// Updates the Inventory in 1 frame
|
|
public func ScheduleUpdateInventory()
|
|
{
|
|
if (!GetEffect("UpdateInventory", this))
|
|
AddEffect("UpdateInventory", this, 1, 1, this);
|
|
}
|
|
|
|
private func FxUpdateInventoryTimer()
|
|
{
|
|
UpdateInventory();
|
|
return FX_Execute_Kill;
|
|
}
|
|
|
|
/* Display */
|
|
|
|
private func UpdateInventory()
|
|
{
|
|
// only display if we have a clonk and it's not disabled
|
|
var clonk = GetCursor(GetOwner());
|
|
if(!clonk || !clonk->GetCrewEnabled())
|
|
{
|
|
if (inventory_gui_menu.Player != NO_OWNER)
|
|
{
|
|
inventory_gui_menu.Player = NO_OWNER;
|
|
GuiUpdate(inventory_gui_menu, inventory_gui_id);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Make sure inventory is visible
|
|
if (inventory_gui_menu.Player != GetOwner())
|
|
{
|
|
inventory_gui_menu.Player = GetOwner();
|
|
GuiUpdate(inventory_gui_menu, inventory_gui_id);
|
|
}
|
|
|
|
UpdateInventoryButtons(clonk);
|
|
|
|
// update inventory-slots
|
|
var hand_item_pos = clonk->~GetHandItemPos(0);
|
|
var quick_switch_slot = clonk->~GetQuickSwitchSlot();
|
|
|
|
for (var slot_info in inventory_slots)
|
|
{
|
|
var item = clonk->GetItem(slot_info.slot);
|
|
// Enable objects to provide a custom overlay for the icon slot.
|
|
// This could e.g. be used by special scenarios or third-party mods.
|
|
var custom_overlay = nil;
|
|
// For stacked objects, we will have multiple virtual objects in one slot.
|
|
var stack_count = nil;
|
|
if (item)
|
|
{
|
|
stack_count = item->~GetStackCount();
|
|
custom_overlay = item->~GetInventoryIconOverlay();
|
|
}
|
|
var needs_selection = hand_item_pos == slot_info.slot;
|
|
var needs_quick_switch = quick_switch_slot == slot_info.slot;
|
|
var has_extra_slot = item && item->~HasExtraSlot();
|
|
if ((!!item == slot_info.empty) || (item != slot_info.obj) || (needs_selection != slot_info.hand) || (needs_quick_switch != slot_info.quick) || (stack_count != slot_info.last_count) || has_extra_slot || slot_info.had_custom_overlay || custom_overlay)
|
|
{
|
|
// Hide or show extra-slot display?
|
|
var extra_slot_player = NO_OWNER;
|
|
var extra_symbol = nil;
|
|
var extra_symbol_stack_count = nil;
|
|
var contents = nil;
|
|
var extra_slot_background_symbol = nil;
|
|
if (has_extra_slot)
|
|
{
|
|
// Show!
|
|
contents = item->Contents(0);
|
|
if (contents)
|
|
{
|
|
extra_symbol = contents;
|
|
// Stack count: Some contents may provide their own stack count function just for inventory display
|
|
extra_symbol_stack_count = contents->~GetContainedDisplayStackCount();
|
|
if (!GetType(extra_symbol_stack_count))
|
|
{
|
|
// Stack count fallback to actually stacked objects
|
|
extra_symbol_stack_count = contents->~GetStackCount();
|
|
}
|
|
}
|
|
extra_slot_player = GetOwner();
|
|
extra_slot_background_symbol = Icon_Menu_Circle;
|
|
// And attach tracker..
|
|
var i = 0, e = nil;
|
|
var found = false;
|
|
while (e = GetEffect("ExtraSlotUpdater", item, i++))
|
|
{
|
|
if (e.CommandTarget != this) continue;
|
|
found = true;
|
|
break;
|
|
}
|
|
if (!found) AddEffect("ExtraSlotUpdater", item, 1, 30 + Random(60), this);
|
|
}
|
|
// What to display in the extra slot?
|
|
var extra_text = nil, number_symbol = nil;
|
|
if (extra_symbol && extra_symbol_stack_count)
|
|
{
|
|
if (contents->IsInfiniteStackCount())
|
|
number_symbol = Icon_Number;
|
|
else extra_text = Format("%dx", extra_symbol_stack_count);
|
|
}
|
|
|
|
// Close a possible lingering custom overlay for that slot.
|
|
var custom_overlay_id = 2000 + slot_info.ID;
|
|
GuiClose(inventory_gui_id, custom_overlay_id, nil);
|
|
|
|
// Compose the update!
|
|
var update =
|
|
{
|
|
slot = { Symbol = item },
|
|
extra_slot =
|
|
{
|
|
Player = extra_slot_player,
|
|
Text = extra_text,
|
|
symbol =
|
|
{
|
|
Symbol = number_symbol
|
|
},
|
|
circle =
|
|
{
|
|
Symbol = extra_slot_background_symbol,
|
|
symbol = { Symbol = extra_symbol }
|
|
}
|
|
},
|
|
count =
|
|
{
|
|
Text = ""
|
|
}
|
|
};
|
|
|
|
if (item)
|
|
{
|
|
if (stack_count > 1 && !item->~IsInfiniteStackCount())
|
|
{
|
|
update.count.Text = Format("%dx", stack_count);
|
|
slot_info.last_count = stack_count;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slot_info.last_count = nil;
|
|
}
|
|
|
|
if (custom_overlay)
|
|
{
|
|
update.overlay = custom_overlay;
|
|
update.overlay.ID = custom_overlay_id;
|
|
slot_info.had_custom_overlay = true;
|
|
}
|
|
else
|
|
{
|
|
slot_info.had_custom_overlay = false;
|
|
}
|
|
|
|
GuiUpdate(update, inventory_gui_id, slot_info.ID, this);
|
|
|
|
var tag = "Std";
|
|
if (needs_quick_switch) tag = "Quick";
|
|
if (needs_selection) tag = "Selected";
|
|
GuiUpdateTag(tag, inventory_gui_id, slot_info.ID, this);
|
|
|
|
slot_info.hand = needs_selection;
|
|
slot_info.quick = needs_quick_switch;
|
|
slot_info.obj = item;
|
|
slot_info.empty = !item;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets the inventory size to the currently selected clonk
|
|
private func UpdateInventoryButtons(object clonk)
|
|
{
|
|
var max_contents_count = clonk.MaxContentsCount;
|
|
|
|
var old_count = GetLength(inventory_slots);
|
|
|
|
// need to create more inventory buttons?
|
|
while (max_contents_count > GetLength(inventory_slots))
|
|
CreateNewInventoryButton(max_contents_count);
|
|
|
|
// need to remove some inventory buttons?
|
|
while (max_contents_count < GetLength(inventory_slots))
|
|
{
|
|
var slot_info = inventory_slots[-1];
|
|
GuiClose(inventory_gui_id, slot_info.ID, this);
|
|
SetLength(inventory_slots, GetLength(inventory_slots)-1);
|
|
}
|
|
|
|
// modifications occured? Adjust position of old slots
|
|
if (old_count != max_contents_count)
|
|
{
|
|
for (var i = 0; i < Min(old_count, max_contents_count); ++i)
|
|
{
|
|
var slot_info = inventory_slots[i];
|
|
var update = CalculateButtonPosition(i, max_contents_count);
|
|
GuiUpdate(update, inventory_gui_id, slot_info.ID, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert an inventory slot into the inventory-bar
|
|
private func CreateNewInventoryButton(int max_slots)
|
|
{
|
|
var slot_number = GetLength(inventory_slots);
|
|
var slot_info =
|
|
{
|
|
slot = slot_number,
|
|
ID = slot_number + 1,
|
|
hand = false,
|
|
quick = false,
|
|
obj = nil,
|
|
empty = true
|
|
};
|
|
PushBack(inventory_slots, slot_info);
|
|
|
|
var slot = AssembleInventoryButton(max_slots, slot_number, slot_info);
|
|
|
|
GuiUpdate({_new_icon = slot}, inventory_gui_id);
|
|
}
|
|
|
|
// Calculates the position of a specific button and returns a proplist.
|
|
private func CalculateButtonPosition(int slot_number, int max_slots)
|
|
{
|
|
var pos_x_offset = -((GUI_Controller_InventoryBar_IconSize + GUI_Controller_InventoryBar_IconMargin) * max_slots - GUI_Controller_InventoryBar_IconMargin) / 2;
|
|
var pos_x = pos_x_offset + (GUI_Controller_InventoryBar_IconSize + GUI_Controller_InventoryBar_IconMargin) * slot_number;
|
|
var pos_y = GUI_Controller_InventoryBar_IconMarginScreenTop;
|
|
var pos =
|
|
{
|
|
Left = Format("50%%%s", ToEmString(pos_x)),
|
|
Top = Format("0%%%s", ToEmString(pos_y)),
|
|
Right = Format("50%%%s", ToEmString(pos_x + GUI_Controller_InventoryBar_IconSize)),
|
|
Bottom = Format("0%%%s", ToEmString(pos_y + GUI_Controller_InventoryBar_IconSize))
|
|
};
|
|
return pos;
|
|
}
|
|
|
|
private func FxExtraSlotUpdaterTimer(object target, proplist effect)
|
|
{
|
|
if (!this) return FX_Execute_Kill;
|
|
if (!target) return FX_Execute_Kill;
|
|
if (target->Contained() != GetCursor(GetOwner())) return FX_Execute_Kill;
|
|
return FX_OK;
|
|
}
|
|
|
|
private func FxExtraSlotUpdaterUpdate(object target, proplist effect)
|
|
{
|
|
if (this) ScheduleUpdateInventory();
|
|
} |