polished the updating behaviour of the interaction menu; added "Your Environment" tab into interaction menu

Controls
David Dormagen 2015-03-05 18:53:52 +01:00
parent 635dd6c285
commit d740a79ba5
8 changed files with 244 additions and 27 deletions

View File

@ -462,6 +462,15 @@ public func GetHandAction()
return false;
}
/* Enable the Clonk to pick up stuff from its surrounding in the interaction menu */
public func OnInteractionMenuOpen(object menu)
{
_inherited(menu, ...);
var surrounding = CreateObject(Helper_Surrounding);
surrounding->InitFor(this, menu);
}
/* Mesh transformations */
local mesh_transformation_list;

View File

@ -0,0 +1,8 @@
[DefCore]
id=Helper_Surrounding
Version=5,2,0,1
Category=0
Width=8
Height=8
Offset=-4,-4
Picture=0,0,64,64

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,163 @@
/**
Surrounding
Enables you to pick up objects from your surrounding.
@author Zapper
*/
// proplist {id = <id>, amount = <amount>}
local current_object_info;
public func GetObjectsMenuEntries()
{
var menu_entries = [];
for (var info in current_object_info)
{
var text = "";
if (info.amount > 1) text = Format("%dx", info.amount);
PushBack(menu_entries, {symbol = info.id, text = text});
}
return menu_entries;
}
public func GetInteractionMenus(object clonk)
{
var menus = _inherited() ?? [];
var menu =
{
title = "$ObjectsAroundYou$",
entries_callback = this.GetObjectsMenuEntries,
callback = "OnPickUpObject",
callback_target = this,
BackgroundColor = RGB(0, 50, 0),
Priority = 20
};
PushBack(menus, menu);
return menus;
}
public func OnPickUpObject(id symbol)
{
var found = nil;
for (var obj in FindObjects(Find_Distance(Radius), Find_ID(symbol), Find_NoContainer(), Sort_Distance()))
{
if (obj->GBackSolid()) continue;
found = obj;
break;
}
// Maybe something else was quicker?
if (!found) return;
var clonk = GetActionTarget();
clonk->Collect(found);
// It is possible that the Clonk removed the object.
if (!found || found->Contained())
Refresh();
}
func InitFor(object clonk, object menu)
{
SetAction("Attach", clonk);
SetOwner(clonk->GetOwner());
// The effects will remove this object once the interaction menu is closed.
AddEffect("KeepAlive", menu, 1, 0, this);
// Refresh the objects regularly.
AddEffect("CheckRefresh", this, 1, 5, this);
// And refresh one time.
Refresh();
}
func Refresh()
{
// Look for all objects in the vicinity that can be accessed by the Clonk (aka non-stuck).
var objects = FindObjects(Find_Distance(Radius), Find_NoContainer(), Find_Category(C4D_Object));
var new_object_info = [];
for (var obj in objects)
{
if (!obj.Collectible) continue;
// Use GBackSolid instead of Stuck, because items dropped by the Clonk are regularly in earth with their bottom vertex (shovel f.e.).
// So be satisfied with free center.
if (obj->GBackSolid()) continue;
// Already in the list? Just increase the amount.
var found = false;
for (var old_data in new_object_info)
{
if (old_data.id != obj->GetID()) continue;
++old_data.amount;
found = true;
break;
}
if (found) continue;
// Otherwise, just add to list.
PushBack(new_object_info, {id = obj->GetID(), amount = 1});
}
if (new_object_info == current_object_info) return;
current_object_info = new_object_info;
UpdateInteractionMenus(this.GetObjectsMenuEntries);
}
func FxCheckRefreshTimer(object target, proplist effect)
{
Refresh();
}
// You can transfer items to the environment, which will then be placed on the ground.
func Collection2(object obj)
{
var clonk = GetActionTarget();
obj->Exit(0, clonk->GetDefBottom() - clonk->GetY());
}
// When the item is moved into this object via script, we don't even have to collect it in the first place.
func Collect(object obj)
{
var clonk = GetActionTarget();
var container = obj->Contained();
if (container)
{
// Special treatment for objects that the Clonk holds. Just use the appropriate library function.
if (container->~IsClonk())
container->DropInventoryItem(container->GetItemPos(obj));
else
// Otherwise, just force-drop it at the Clonk's feet.
obj->Exit(0, clonk->GetDefBottom() - clonk->GetY());
}
else
obj->SetPosition(clonk->GetX(), clonk->GetDefBottom());
return true;
}
func AttachTargetLost()
{
RemoveObject();
}
func FxKeepAliveStop(object target, proplist effect, int reason, int temp)
{
if (temp) return;
RemoveObject();
}
local Name = "$Name$";
local Description = "$Description$";
local Plane = 1;
local Radius = 40;
local ActMap =
{
Attach =
{
Prototype = Action,
Name="Attach",
Procedure=DFA_ATTACH,
NextAction="Hold",
Length=1,
FacetBase=1,
AbortCall = "AttachTargetLost"
}
};

View File

@ -0,0 +1,3 @@
Name=Umgebung
Description=Erlaubt das Aufnehmen von Objekten aus der Umgebung.
ObjectsAroundYou=Objekte in der Umgebung

View File

@ -0,0 +1,3 @@
Name=Surrounding
Description=Enables you to pick up objects from your surrounding.
ObjectsAroundYou=Objects Around You

View File

@ -19,10 +19,8 @@ local current_objects;
current_menus is an array with two fields
each field contain a proplist with the attributes:
target: the target object, needs to be in current_objects
menu_object: target of the menu (usually a dummy object)
menu_object: target of the menu (usually a dummy object) (the ID is always 1; the menu's sidebar has the ID 2)
menu_id
sidebar: proplist with the following attributes
menu_object
menus: array with more proplists with the following attributes:
type: flag (needed for content-menus f.e.)
title
@ -96,7 +94,12 @@ func CreateFor(object cursor)
func Init(object cursor)
{
this.cursor = cursor;
AddEffect("IntCheckObjects", cursor, 1, 10, this);
var checking_effect = AddEffect("IntCheckObjects", cursor, 1, 10, this);
// Notify the Clonk. This can be used to create custom entries in the objects list via helper objects. For example the "Your Environment" tab.
// Note that the cursor is NOT informed when the menu is closed again. Helper objects can be attached to the livetime of this menu by, f.e., effects.
cursor->~OnInteractionMenuOpen(this);
// And then quickly refresh for the very first time. Successive refreshs will be only every 10 frames.
EffectCall(cursor, checking_effect, "Timer");
}
func FxIntCheckObjectsStart(target, effect, temp)
@ -107,10 +110,20 @@ func FxIntCheckObjectsStart(target, effect, temp)
func FxIntCheckObjectsTimer(target, effect, timer)
{
var new_objects = FindObjects(Find_AtPoint(target->GetX(), target->GetY()), Find_NoContainer(), Find_Or(Find_Category(C4D_Vehicle), Find_Category(C4D_Structure), Find_Func("IsContainer"), Find_Func("IsClonk")));
if (new_objects == current_objects) return;
var new_objects = FindObjects(Find_AtPoint(target->GetX(), target->GetY()), Find_NoContainer(), Find_Or(Find_Category(C4D_Vehicle), Find_Category(C4D_Structure), Find_Func("IsContainer"), Find_Func("IsClonk"), Find_ActionTarget(target)));
var equal = GetLength(new_objects) == GetLength(current_objects);
UpdateObjects(new_objects);
if (equal)
{
for (var i = GetLength(new_objects) - 1; i >= 0; --i)
{
if (new_objects[i] == current_objects[i]) continue;
equal = false;
break;
}
}
if (!equal)
UpdateObjects(new_objects);
}
// updates the objects shown in the side bar
@ -142,7 +155,12 @@ func UpdateObjects(array new_objects)
// need to fill an empty menu slot?
for (var i = 0; i < 2; ++i)
{
if (current_menus[i] != nil) continue;
// If the menu already exists, just update the sidebar.
if (current_menus[i] != nil)
{
RefreshSidebar(i);
continue;
}
// look for next object to fill slot
for (var obj in current_objects)
{
@ -189,12 +207,13 @@ func OpenMenuForObject(object obj, int slot)
else
++effect_index;
}
// create a menu with all interaction possibilities for an object
// always recreate the side bar
var sidebar = CreateSideBar(slot);
// Create a menu with all interaction possibilities for an object.
// Always create the side bar AFTER the main menu, so that the target can be copied.
var main = CreateMainMenu(obj, slot);
// to close the part_menu automatically when the main menu is closed
// To close the part_menu automatically when the main menu is closed. The sidebar will use this target, too.
current_menus[slot].menu_object = main.Target;
// Now, the sidebar.
var sidebar = CreateSideBar(slot);
var sidebar_size_em = ToEmString(InteractionMenu_SideBarSize);
var part_menu =
@ -202,7 +221,8 @@ func OpenMenuForObject(object obj, int slot)
Left = "0%", Right = "50%-2em",
Bottom = "100%-14em",
sidebar = sidebar, main = main,
Target = current_menus[slot].menu_object
Target = current_menus[slot].menu_object,
ID = 1
};
if (slot == 1)
@ -224,7 +244,7 @@ func OpenMenuForObject(object obj, int slot)
var root_menu =
{
one_part = part_menu,
_one_part = part_menu,
Target = this,
Decoration = GUI_MenuDeco,
description_box =
@ -258,7 +278,7 @@ func OpenMenuForObject(object obj, int slot)
}
else // menu already exists and only one part has to be added
{
GuiUpdate({update = part_menu}, current_main_menu_id, nil, nil);
GuiUpdate({_update = part_menu}, current_main_menu_id, nil, nil);
}
}
@ -268,14 +288,13 @@ func OpenMenuForObject(object obj, int slot)
func CreateSideBar(int slot)
{
var em_size = ToEmString(InteractionMenu_SideBarSize);
var dummy = CreateDummy();
var sidebar =
{
Priority = 10,
Right = em_size,
Style = GUI_VerticalLayout,
Target = dummy,
OnClose = GuiAction_Call(this, "RemoveDummy", dummy),
Target = current_menus[slot].menu_object,
ID = 2
};
if (slot == 1)
{
@ -316,6 +335,17 @@ func CreateSideBar(int slot)
return sidebar;
}
// Updates the sidebar with the current objects (and closes the old one).
func RefreshSidebar(int slot)
{
if (!current_menus[slot]) return;
// Close old sidebar? This call will just do nothing if there is no sidebar present.
GuiClose(current_main_menu_id, 2, current_menus[slot].menu_object);
var sidebar = CreateSideBar(slot);
GuiUpdate({sidebar = sidebar}, current_main_menu_id, 1, current_menus[slot].menu_object);
}
func OnSidebarEntrySelected(data, int player, int ID, int subwindowID, object target)
{
if (!data.obj) return;
@ -435,7 +465,7 @@ func CreateMainMenu(object obj, int slot)
for (var i = 0; i < GetLength(menus); ++i)
{
if (!(menus[i].flag & InteractionMenu_Contents)) continue;
AddEffect("IntRefreshContentsMenu", this, 1, 5, this, nil, obj, slot, i);
AddEffect("IntRefreshContentsMenu", this, 1, 1, this, nil, obj, slot, i);
}
return container;
@ -525,7 +555,9 @@ func FxIntRefreshContentsMenuStart(object target, proplist effect, temp, object
func FxIntRefreshContentsMenuTimer(target, effect, time)
{
if (!effect.obj) return -1;
// The fast interval is only used for the very first check to ensure a fast startup.
// It can't be just called instantly though, because the menu might not have been opened yet.
if (effect.Interval == 1) effect.Interval = 5;
var inventory = [];
var obj, i = 0;
while (obj = effect.obj->Contents(i++))

View File

@ -85,17 +85,16 @@ public func GetProductionMenuEntries()
info = nil;
}
// Prepare menu entry.
var entry =
var entry = new custom_entry
{
Prototype = custom_entry,
image = {Prototype = custom_entry.image},
controls =
image = new custom_entry.image{},
controls = new custom_entry.controls
{
Prototype = custom_entry.controls,
remove = {Prototype = custom_entry.controls.remove},
endless = {Prototype = custom_entry.controls.endless}
remove = new custom_entry.controls.remove{},
endless = new custom_entry.controls.endless{},
}
};
entry.image.Symbol = product;
if (info) // Currently in queue?
{