forked from Mirrors/openclonk
improved menu functionality; added test scenario and helper objects
parent
d3dba14323
commit
c2ba7e65d7
|
@ -0,0 +1,7 @@
|
|||
[DefCore]
|
||||
id=HUD_MenuStyle_Classic
|
||||
Version=4,10,0,0
|
||||
Category=C4D_StaticBack
|
||||
Width=1
|
||||
Height=1
|
||||
Offset=-1,-1
|
Binary file not shown.
After Width: | Height: | Size: 124 B |
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
Classic
|
||||
Mimics the interface to a classic menu.
|
||||
*/
|
||||
|
||||
local Name = "Classic Menu";
|
||||
|
||||
// set on creation
|
||||
local menu_def, permanent;
|
||||
local menu_layout;
|
||||
local target;
|
||||
|
||||
// set later
|
||||
local menu_id;
|
||||
local entries;
|
||||
|
||||
func Construction()
|
||||
{
|
||||
entries = [];
|
||||
}
|
||||
|
||||
global func CreateClassicMenu(id symbol, object command_object, int extra, string caption, int extra_data, int style, bool permanent, id menu_id)
|
||||
{
|
||||
if (!this) return;
|
||||
var menu = CreateObject(HUD_MenuStyle_Classic, 0, 0, GetOwner());
|
||||
menu.Visibility = VIS_Owner;
|
||||
menu.menu_def = menu_id;
|
||||
menu.permanent = permanent;
|
||||
menu.target = this;
|
||||
|
||||
menu.menu_layout =
|
||||
{
|
||||
BackgroundColor = 0x50553300,
|
||||
Decoration = GUI_MenuDeco,
|
||||
Target = menu,
|
||||
inner =
|
||||
{
|
||||
header =
|
||||
{
|
||||
Hgt = [0, 32],
|
||||
icon = {Symbol = symbol, Wdt = [0, 32], Hgt = [0, 32]},
|
||||
caption = {X = [0, 32], Text = caption, Style = MENU_TextVCenter}
|
||||
},
|
||||
body =
|
||||
{
|
||||
Y = [0, 32],
|
||||
items =
|
||||
{
|
||||
Wdt = 500,
|
||||
Style = MENU_GridLayout
|
||||
},
|
||||
description =
|
||||
{
|
||||
ID = 1,
|
||||
Target = 0,
|
||||
X = 500,
|
||||
Text = "Empty"
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
Menu_AddMargin(menu.menu_layout.inner, 15, 15);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public func AddMenuItem(string caption, string command, symbol, int count, parameter, string info_caption, int extra, XPar1, XPar2)
|
||||
{
|
||||
var ID = GetLength(entries) + 1;
|
||||
var entry =
|
||||
{
|
||||
Target = target, // needed for the call
|
||||
ID = ID,
|
||||
BackgroundColor = {Std = 0, Hover = 0x50ff0000},
|
||||
Symbol = symbol,
|
||||
Wdt = [0, 64],
|
||||
Hgt = [0, 64],
|
||||
Text = Format("%dx", count),
|
||||
Priority = ID,
|
||||
OnClick = MenuAction_Call(this, "OnClick", [symbol, ID, command, parameter]),
|
||||
OnMouseIn = [MenuAction_SetTag(nil, 0, "Hover"), MenuAction_Call(this, "UpdateDesc")],
|
||||
OnMouseOut = MenuAction_SetTag(nil, 0, "Std"),
|
||||
};
|
||||
entries[ID] = [info_caption ?? symbol.Description];
|
||||
|
||||
menu_layout.inner.body.items[Format("child%d", ID)] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
func Open()
|
||||
{
|
||||
return CustomMenuOpen(menu_layout);
|
||||
}
|
||||
|
||||
func UpdateDesc(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
var update = { Text = entries[subwindowID][0] };
|
||||
CustomMenuUpdate(update, ID, 1, 0);
|
||||
}
|
||||
|
||||
func OnClick(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
target->Call(data[2], data[0], data[3]);
|
||||
if (!permanent)
|
||||
CustomMenuClose(ID);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
Name=Classic
|
||||
Description=Imitiert ein Interface ähnlich des klassischen Menüs.
|
|
@ -0,0 +1,2 @@
|
|||
Name=Classic
|
||||
Description=Mimics the interface to a classic menu.
|
|
@ -0,0 +1 @@
|
|||
This folder contains some easy-to-use styles for working with menus.
|
|
@ -0,0 +1,7 @@
|
|||
[DefCore]
|
||||
id=MenuStyle_List
|
||||
Version=4,10,0,0
|
||||
Category=C4D_StaticBack
|
||||
Width=1
|
||||
Height=1
|
||||
Offset=-1,-1
|
Binary file not shown.
After Width: | Height: | Size: 124 B |
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
List
|
||||
Shows a simple list menu.
|
||||
*/
|
||||
|
||||
local Name = "List Menu";
|
||||
|
||||
local entries;
|
||||
local on_mouse_over_callback, on_mouse_out_callback;
|
||||
local on_close_callback;
|
||||
local permanent;
|
||||
|
||||
local menu_id;
|
||||
|
||||
func Construction()
|
||||
{
|
||||
entries = [];
|
||||
this.Style = MENU_VerticalLayout;
|
||||
this.Target = this;
|
||||
}
|
||||
|
||||
func Close()
|
||||
{
|
||||
if (menu_id)
|
||||
CustomMenuClose(menu_id);
|
||||
if (on_close_callback && on_close_callback[0])
|
||||
on_close_callback[0]->Call(on_close_callback[1], on_close_callback[2]);
|
||||
RemoveObject();
|
||||
}
|
||||
|
||||
func SetPermanent(bool perm) { permanent = perm ?? true; }
|
||||
|
||||
func SetCloseCallback(proplist target, callback, parameter)
|
||||
{
|
||||
on_close_callback = [target, callback, parameter];
|
||||
}
|
||||
|
||||
func SetMouseOverCallback(proplist target, callback)
|
||||
{
|
||||
on_mouse_over_callback = [target, callback];
|
||||
}
|
||||
|
||||
func SetMouseOutCallback(proplist target, callback)
|
||||
{
|
||||
on_mouse_out_callback = [target, callback];
|
||||
}
|
||||
|
||||
func AddItem(symbol, string text, user_ID, proplist target, command, parameter, custom_entry)
|
||||
{
|
||||
var on_hover = MenuAction_SetTag(nil, 0, "OnHover");
|
||||
if (on_mouse_over_callback)
|
||||
on_hover = [on_hover, MenuAction_Call(this, "DoCallback", on_mouse_over_callback)];
|
||||
var on_hover_stop = MenuAction_SetTag(nil, 0, "Std");
|
||||
if (on_mouse_out_callback)
|
||||
on_hover_stop = [on_hover_stop, MenuAction_Call(this, "DoCallback", on_mouse_out_callback)];
|
||||
|
||||
var ID = GetLength(entries) + 1;
|
||||
if (!custom_entry)
|
||||
{
|
||||
custom_entry = {Hgt = [0, 64], sym = {Wdt = [0, 64], Hgt = [0, 64]}, desc = {X = [0, 64]}};
|
||||
custom_entry.sym.Symbol = symbol;
|
||||
custom_entry.desc.Text = text;
|
||||
custom_entry.desc.Style = MENU_TextVCenter;
|
||||
custom_entry.Style = MENU_FitChildren;
|
||||
custom_entry.ID = ID;
|
||||
custom_entry.Target = this;
|
||||
custom_entry.Priority = ID;
|
||||
custom_entry.BackgroundColor = {Std = 0, OnHover = 0x50ff0000};
|
||||
custom_entry.OnClick = MenuAction_Call(this, "OnClick");
|
||||
custom_entry.OnMouseIn = on_hover;
|
||||
custom_entry.OnMouseOut = on_hover_stop;
|
||||
}
|
||||
entries[ID - 1] = [target, command, parameter, user_ID];
|
||||
this[Format("menuChild%d", ID)] = custom_entry;
|
||||
|
||||
return custom_entry;
|
||||
}
|
||||
|
||||
func DoCall(int ID, command, proplist target, bool noclose, int player)
|
||||
{
|
||||
var self = this; // safety
|
||||
var entry = entries[ID - 1];
|
||||
target = target ?? entry[0];
|
||||
// target removed? safety first!
|
||||
if (target)
|
||||
{
|
||||
if (target->Call(command ?? entry[1], entry[2], entry[3], player) == -1) return;
|
||||
}
|
||||
Log("self: %v, noclose: %d, permanent: %d", self, noclose, permanent);
|
||||
if (self)
|
||||
if (!noclose && !permanent)
|
||||
Close();
|
||||
}
|
||||
|
||||
func OnClick(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
DoCall(subwindowID, nil, nil, nil, player);
|
||||
}
|
||||
|
||||
func DoCallback(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
DoCall(subwindowID, data[1], data[0], true, player);
|
||||
}
|
||||
|
||||
func Open()
|
||||
{
|
||||
menu_id = CustomMenuOpen(this);
|
||||
return menu_id;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
Name=List
|
||||
Description=Ein einfaches Listenmenü.
|
|
@ -0,0 +1,2 @@
|
|||
Name=List
|
||||
Description=Shows a simple list menu.
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
This file contains functions that are used for layouting custom menus.
|
||||
*/
|
||||
|
||||
global func CreateCustomMenu(id menuStyle)
|
||||
{
|
||||
var menu = CreateObject(menuStyle);
|
||||
menu->SetPosition(1, 1);
|
||||
return menu;
|
||||
}
|
||||
|
||||
global func MenuAction_Call(proplist target, string function, value)
|
||||
{
|
||||
return [MENU_Call, target, function, value];
|
||||
}
|
||||
|
||||
global func MenuAction_SetTag(object target, int subwindow, string tag)
|
||||
{
|
||||
return [MENU_SetTag, target, subwindow, tag];
|
||||
}
|
||||
|
||||
global func Menu_AddMargin(proplist submenu, int marginX, int marginY)
|
||||
{
|
||||
submenu.X = submenu.X ?? [0, 0];
|
||||
submenu.Y = submenu.Y ?? [0, 0];
|
||||
submenu.Wdt = submenu.Wdt ?? [1000, 0];
|
||||
submenu.Hgt = submenu.Hgt ?? [1000, 0];
|
||||
|
||||
// safety, the coordinates could be a single value
|
||||
if (GetType(submenu.X) != C4V_Array) submenu.X = [submenu.X, 0];
|
||||
if (GetType(submenu.Y) != C4V_Array) submenu.Y = [submenu.Y, 0];
|
||||
if (GetType(submenu.Wdt) != C4V_Array) submenu.Wdt = [submenu.Wdt, 0];
|
||||
if (GetType(submenu.Hgt) != C4V_Array) submenu.Hgt = [submenu.Hgt, 0];
|
||||
|
||||
submenu.X[1] += marginX;
|
||||
submenu.Y[1] += marginY;
|
||||
submenu.Wdt[1] -= marginX;
|
||||
submenu.Hgt[1] -= marginY;
|
||||
return true;
|
||||
}
|
||||
|
||||
global func Menu_UpdateText(string text, int menu, int submenu, object target)
|
||||
{
|
||||
var update = {Text = text};
|
||||
CustomMenuUpdate(update, menu, submenu, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
// adds proplist /submenu/ as a new property to /menu/
|
||||
global func Menu_AddSubmenu(proplist submenu, proplist menu)
|
||||
{
|
||||
do
|
||||
{
|
||||
var uniqueID = Format("child%d", RandomX(10000, 0xffffff));
|
||||
if (menu[uniqueID] != nil) continue;
|
||||
menu[uniqueID] = submenu;
|
||||
return true;
|
||||
} while (true);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
[Scenario]
|
||||
Title=Menu Test
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
static active_menu;
|
||||
|
||||
func Initialize()
|
||||
{
|
||||
var starter_menu =
|
||||
{
|
||||
Style = MENU_Multiple,
|
||||
X = [1000], Y = [0, -100],
|
||||
Wdt = [1000, 200], Hgt = [0, 100],
|
||||
text = {Style = MENU_TextVCenter | MENU_TextHCenter, Text = "OPEN MENU"}
|
||||
};
|
||||
}
|
||||
/* -------------------------------- MAIN ----------------------------- */
|
||||
|
||||
func InitializePlayer(plr)
|
||||
{
|
||||
Schedule(nil, "Scenario->StartMenu()", 5, 0);
|
||||
}
|
||||
|
||||
func MainOnHover(parameter, int ID)
|
||||
{
|
||||
Menu_UpdateText(parameter, active_menu, 9999);
|
||||
}
|
||||
func StartMenu(plr)
|
||||
{
|
||||
var main_menu =
|
||||
{
|
||||
Decoration = GUI_MenuDeco,
|
||||
head = {Hgt = [0, 50], Text = "Please choose a test!", Style = MENU_TextHCenter | MENU_TextVCenter, IDs = 0},
|
||||
body = {Y = [0, 60], right = {X = 500, BackgroundColor = 0x50ffffff } },
|
||||
};
|
||||
var menu = CreateCustomMenu(MenuStyle_List);
|
||||
main_menu.body.left = menu;
|
||||
|
||||
menu.Wdt = 500;
|
||||
menu->SetMouseOverCallback(Scenario, "MainOnHover");
|
||||
menu->AddItem(Chest, "Test Multiple Lists (Inventory)", nil, Scenario, "StartMultipleListTest", "Shows multiple list-style menus in one big menu.");
|
||||
menu->AddItem(Rule_TeamAccount, "Test Client/Host (Scenario Options)", nil, Scenario, "StartScenarioOptionsTest", "Shows how to display a dialogue that behaves differently for players.");
|
||||
|
||||
active_menu = CustomMenuOpen(main_menu);
|
||||
}
|
||||
|
||||
/* ------------------------ inventory test ----------------------------- */
|
||||
static selected_inventory, inv_menus;
|
||||
func StartMultipleListTest()
|
||||
{
|
||||
CustomMenuClose(active_menu);
|
||||
selected_inventory = [];
|
||||
inv_menus = [];
|
||||
// layout: headline and four sections with items
|
||||
var menu =
|
||||
{
|
||||
head = { ID = 999, Hgt = [0, 50], Text = "Inventory: <c ff0000>Empty</c>", Style = MENU_TextHCenter | MENU_TextVCenter, BackgroundColor = 0x55000000},
|
||||
contents = { Y = [0, 50], X = [0, 20], Wdt = [1000, -20] },
|
||||
};
|
||||
|
||||
var inventory = [[Sword, Axe, Club], [IronBomb, Dynamite, Boompack, Firestone], [Bow, Musket, Javelin], [Shield, Bread, Sproutberry, CookedMushroom]];
|
||||
var x = [0, [500, 20], 0, [500, 20]], y = [0, 0, [500, 20], [500, 20]], w = [[500, -20], 1000, [500, -20], 1000], h = [[500, -20], [500, -20], 1000, 1000];
|
||||
for (var i = 0; i < 4; ++i)
|
||||
{
|
||||
var inv = inventory[i];
|
||||
var ID = 9000 + i;
|
||||
var deco = { Decoration = GUI_MenuDeco, X = x[i], Y = y[i], Wdt = w[i], Hgt = h[i] };
|
||||
var m = CreateCustomMenu(MenuStyle_List);
|
||||
deco.menu = m;
|
||||
Menu_AddSubmenu(deco, menu.contents);
|
||||
PushBack(inv_menus, m); // remember for later
|
||||
Menu_AddMargin(m, 20, 20);
|
||||
for (var obj in inv)
|
||||
m->AddItem(obj, obj.Description, nil, Scenario, "SelectInventory", [obj, ID]);
|
||||
}
|
||||
active_menu = CustomMenuOpen(menu);
|
||||
}
|
||||
|
||||
func SelectInventory(info)
|
||||
{
|
||||
var obj = info[0];
|
||||
var ID = info[1];
|
||||
PushBack(selected_inventory, obj);
|
||||
var text = "Your inventory: ";
|
||||
for (var item in selected_inventory)
|
||||
text = Format("%s %s,", text, item.Name);
|
||||
if (GetLength(selected_inventory) == 4)
|
||||
{
|
||||
Log("HERO! YOU WILL SPAWN NOW! %s", text);
|
||||
for (var m in inv_menus)
|
||||
if (m) m->Close();
|
||||
CustomMenuClose(active_menu);
|
||||
}
|
||||
else
|
||||
{
|
||||
var update = { Text = text };
|
||||
CustomMenuUpdate(update, active_menu, 999);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------ scenario options test ----------------------------- */
|
||||
static scenoptions_dummies;
|
||||
func StartScenarioOptionsTest(parameter, int ID, int player)
|
||||
{
|
||||
CustomMenuClose(active_menu);
|
||||
scenoptions_dummies = [];
|
||||
scenoptions_dummies[0] = CreateObject(Dummy, nil, nil, player);
|
||||
scenoptions_dummies[1] = CreateObject(Dummy, nil, nil, player);
|
||||
|
||||
for (var i = 0; i <= 1; ++i)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
scenoptions_dummies[i]->SetOwner(player);
|
||||
scenoptions_dummies[i].Visibility = VIS_Owner;
|
||||
}
|
||||
else
|
||||
{
|
||||
var vis = [VIS_Select];
|
||||
for (var p = 0; p <= GetPlayerCount(); ++p)
|
||||
{
|
||||
var plr = GetPlayerByIndex(p);
|
||||
if (plr == player) continue;
|
||||
vis[plr + 1] = 1;
|
||||
}
|
||||
scenoptions_dummies[i].Visibility = vis;
|
||||
}
|
||||
}
|
||||
var menu =
|
||||
{
|
||||
list =
|
||||
{
|
||||
Wdt = 500,
|
||||
Style = MENU_VerticalLayout,
|
||||
},
|
||||
right = {
|
||||
X = 500,
|
||||
Decoration = GUI_MenuDeco,
|
||||
hostdesc =
|
||||
{
|
||||
ID = 1,
|
||||
Target = scenoptions_dummies[0],
|
||||
Text = "Please select the scenario options!"
|
||||
},
|
||||
clientdesc =
|
||||
{
|
||||
|
||||
ID = 1,
|
||||
Target = scenoptions_dummies[1],
|
||||
Text = Format("%s can set the options now! Please wait!", GetTaggedPlayerName(player))
|
||||
}
|
||||
}
|
||||
};
|
||||
Menu_AddMargin(menu.right.hostdesc, 25, 25);
|
||||
Menu_AddMargin(menu.right.clientdesc, 25, 25);
|
||||
|
||||
var def, rules =[], i = 0;
|
||||
while (def = GetDefinition(i++))
|
||||
{
|
||||
if (!(def->GetCategory() & C4D_Rule)) continue;
|
||||
PushBack(rules, {def = def, ID = i});
|
||||
}
|
||||
for (var rule in rules)
|
||||
{
|
||||
var subm =
|
||||
{
|
||||
ID = rule.ID,
|
||||
Hgt = [0, 64],
|
||||
icon = {Priority = 10, Symbol = rule.def, Wdt = [0, 64], Hgt = [0, 64]},
|
||||
text = {Priority = 10, X = [0, 64], Style = MENU_TextVCenter, Text = rule.def.Name},
|
||||
|
||||
selector = // only visible for host
|
||||
{
|
||||
Target = scenoptions_dummies[0],
|
||||
Priority = 1,
|
||||
BackgroundColor = {Std = 0, Hover = 0x50ff0000, On = 0x5000ff00},
|
||||
OnMouseIn = {
|
||||
Std = [MenuAction_Call(Scenario, "ScenOptsUpdateDesc", [rule.def, rule.ID, false]), MenuAction_SetTag(nil, nil, "Hover")],
|
||||
On = MenuAction_Call(Scenario, "ScenOptsUpdateDesc", [rule.def, rule.ID, true])
|
||||
},
|
||||
OnMouseOut = { Hover = MenuAction_SetTag(nil, nil, "Std"), On = nil },
|
||||
OnClick = {
|
||||
Hover = [MenuAction_Call(Scenario, "ScenOptsActivate", [rule.def, rule.ID]), MenuAction_SetTag(nil, nil, "On")],
|
||||
On = [MenuAction_Call(Scenario, "ScenOptsDeactivate", [rule.def, rule.ID]), MenuAction_SetTag(nil, nil, "Hover")],
|
||||
},
|
||||
}
|
||||
};
|
||||
Menu_AddSubmenu(subm, menu.list);
|
||||
}
|
||||
|
||||
active_menu = CustomMenuOpen(menu);
|
||||
}
|
||||
|
||||
func ScenOptsActivate(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
if (!ObjectCount(Find_ID(data[0])))
|
||||
CreateObject(data[0]);
|
||||
}
|
||||
|
||||
func ScenOptsDeactivate(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
RemoveAll(Find_ID(data[0]));
|
||||
}
|
||||
|
||||
func ScenOptsUpdateDesc(int player, int ID, int subwindowID, object target, data)
|
||||
{
|
||||
var text = "<c ff0000>Do you really want to remove the rule???</c>";
|
||||
if (!data[2])
|
||||
text = data[0].Description;
|
||||
Menu_UpdateText(text, active_menu, 1, scenoptions_dummies[0]);
|
||||
}
|
|
@ -49,6 +49,7 @@
|
|||
#include <C4PlayerList.h>
|
||||
#include <C4GameObjects.h>
|
||||
#include <C4GameControl.h>
|
||||
#include <C4MenuWindow.h>
|
||||
|
||||
#ifndef NOAULDEBUG
|
||||
#include <C4AulDebug.h>
|
||||
|
@ -418,6 +419,45 @@ void C4ControlPlayerCommand::CompileFunc(StdCompiler *pComp)
|
|||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iAddMode), "AddMode", 0));
|
||||
C4ControlPacket::CompileFunc(pComp);
|
||||
}
|
||||
// *** C4ControlMenuCommand
|
||||
|
||||
C4ControlMenuCommand::C4ControlMenuCommand(int32_t actionID, int32_t player, int32_t menuID, int32_t subwindowID, C4Object *target, unsigned int tag, int32_t actionType)
|
||||
: actionID(actionID), player(player), menuID(menuID), subwindowID(subwindowID), target(target ? target->Number : 0), tag(static_cast<int32_t>(tag)), actionType(actionType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void C4ControlMenuCommand::Execute() const
|
||||
{
|
||||
LogF("C4ControlMenuCommand::Execute: %d::%d::%d, %d", menuID, subwindowID, actionID, target);
|
||||
// invalid action? The action needs to be in bounds!
|
||||
if (actionType < 0 || actionType >= C4MenuWindowPropertyName::_lastProp)
|
||||
{
|
||||
// this could only come from a malicious attempt to crash the engine!
|
||||
Log("Warning: invalid action type for C4ControlMenuCommand!");
|
||||
return;
|
||||
}
|
||||
C4MenuWindow *menu = ::MenuWindowRoot.GetChildByID(menuID);
|
||||
// menu was closed?
|
||||
if (!menu) return;
|
||||
|
||||
C4Object *obj = target ? ::Objects.ObjectPointer(target) : 0;
|
||||
// target has been removed in the meantime? abort now
|
||||
if (target && !obj) return;
|
||||
|
||||
menu->ExecuteCommand(actionID, player, subwindowID, actionType, obj, static_cast<unsigned int>(tag));
|
||||
}
|
||||
|
||||
void C4ControlMenuCommand::CompileFunc(StdCompiler *pComp)
|
||||
{
|
||||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(actionID), "ID", -1));
|
||||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(player), "Player", -1));
|
||||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(menuID), "Menu", 0));
|
||||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(subwindowID), "Window", 0));
|
||||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(actionType), "Action", 0));
|
||||
pComp->Value(mkNamingAdapt(target, "Target", 0));
|
||||
C4ControlPacket::CompileFunc(pComp);
|
||||
}
|
||||
|
||||
// *** C4ControlSyncCheck
|
||||
|
||||
|
|
|
@ -214,6 +214,19 @@ public:
|
|||
DECLARE_C4CONTROL_VIRTUALS
|
||||
};
|
||||
|
||||
class C4ControlMenuCommand : public C4ControlPacket // sync
|
||||
{
|
||||
public:
|
||||
C4ControlMenuCommand()
|
||||
: menuID(0), subwindowID(0) { }
|
||||
C4ControlMenuCommand(int32_t actionID, int32_t player, int32_t menuID, int32_t subwindowID,
|
||||
C4Object *target, unsigned int tag, int32_t actionType);
|
||||
protected:
|
||||
int32_t actionID, player, menuID, subwindowID, target, tag, actionType;
|
||||
public:
|
||||
DECLARE_C4CONTROL_VIRTUALS
|
||||
};
|
||||
|
||||
class C4ControlSyncCheck : public C4ControlPacket // not sync
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -126,7 +126,7 @@ void C4GraphicsSystem::Execute()
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Reset object audibility
|
||||
::Objects.ResetAudibility();
|
||||
|
||||
|
@ -182,6 +182,7 @@ void C4GraphicsSystem::Default()
|
|||
ShowPathfinder=false;
|
||||
ShowNetstatus=false;
|
||||
ShowSolidMask=false;
|
||||
ShowMenuInfo=false;
|
||||
ShowHelp=false;
|
||||
FlashMessageText[0]=0;
|
||||
FlashMessageTime=0; FlashMessageX=FlashMessageY=0;
|
||||
|
@ -306,6 +307,7 @@ void C4GraphicsSystem::DeactivateDebugOutput()
|
|||
ShowPathfinder=false; // allow pathfinder! - why this??
|
||||
ShowSolidMask=false;
|
||||
ShowNetstatus=false;
|
||||
ShowMenuInfo=false;
|
||||
}
|
||||
|
||||
void C4GraphicsSystem::DrawHoldMessages()
|
||||
|
@ -398,7 +400,7 @@ void C4GraphicsSystem::DrawHelp()
|
|||
strText.AppendFormat("\n\n[%s]\n\n", "Debug");
|
||||
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgModeToggle").getData(), LoadResStr("IDS_CTL_DEBUGMODE"));
|
||||
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowVtxToggle").getData(), "Entrance+Vertices");
|
||||
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowActionToggle").getData(), "Actions/Commands/Pathfinder");
|
||||
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowActionToggle").getData(), "Actions/Commands/Pathfinder/Menu Info");
|
||||
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowSolidMaskToggle").getData(), "SolidMasks");
|
||||
pDraw->TextOut(strText.getData(), ::GraphicsResource.FontRegular, 1.0, FullScreen.pSurface,
|
||||
iX + iWdt/2 + 64, iY + 64, C4Draw::DEFAULT_MESSAGE_COLOR, ALeft);
|
||||
|
@ -422,14 +424,16 @@ bool C4GraphicsSystem::ToggleShowVertices()
|
|||
bool C4GraphicsSystem::ToggleShowAction()
|
||||
{
|
||||
if (!Game.DebugMode && !Console.Active) { FlashMessage(LoadResStr("IDS_MSG_NODEBUGMODE")); return false; }
|
||||
if (!(ShowAction || ShowCommand || ShowPathfinder))
|
||||
if (!(ShowAction || ShowCommand || ShowPathfinder || ShowMenuInfo))
|
||||
{ ShowAction = true; FlashMessage("Actions"); }
|
||||
else if (ShowAction)
|
||||
{ ShowAction = false; ShowCommand = true; FlashMessage("Commands"); }
|
||||
else if (ShowCommand)
|
||||
{ ShowCommand = false; ShowPathfinder = true; FlashMessage("Pathfinder"); }
|
||||
else if (ShowPathfinder)
|
||||
{ ShowPathfinder = false; FlashMessageOnOff("Actions/Commands/Pathfinder", false); }
|
||||
{ ShowPathfinder = false; ShowMenuInfo = true; FlashMessage("Menu Info"); }
|
||||
else if (ShowMenuInfo)
|
||||
{ ShowMenuInfo = false; FlashMessageOnOff("Actions/Commands/Pathfinder", false); }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
bool ShowPathfinder;
|
||||
bool ShowNetstatus;
|
||||
bool ShowSolidMask;
|
||||
bool ShowMenuInfo;
|
||||
C4Video Video;
|
||||
C4LoaderScreen *pLoaderScreen;
|
||||
void Default();
|
||||
|
|
|
@ -2632,7 +2632,16 @@ C4ScriptConstDef C4ScriptGameConstMap[]=
|
|||
|
||||
{ "MENU_SetTag" ,C4V_Int, C4MenuWindowActionID::SetTag },
|
||||
{ "MENU_Call" ,C4V_Int, C4MenuWindowActionID::Call },
|
||||
|
||||
{ "MENU_GridLayout" ,C4V_Int, C4MenuWindowStyleFlag::Grid },
|
||||
{ "MENU_VerticalLayout" ,C4V_Int, C4MenuWindowStyleFlag::Vertical },
|
||||
{ "MENU_TextVCenter" ,C4V_Int, C4MenuWindowStyleFlag::TextVCenter },
|
||||
{ "MENU_TextHCenter" ,C4V_Int, C4MenuWindowStyleFlag::TextHCenter },
|
||||
{ "MENU_TextRight" ,C4V_Int, C4MenuWindowStyleFlag::TextRight },
|
||||
{ "MENU_TextBottom" ,C4V_Int, C4MenuWindowStyleFlag::TextBottom },
|
||||
{ "MENU_TextTop" ,C4V_Int, C4MenuWindowStyleFlag::None }, // note that top and left are considered default
|
||||
{ "MENU_TextLeft" ,C4V_Int, C4MenuWindowStyleFlag::None }, // they are only included for completeness
|
||||
{ "MENU_FitChildren" ,C4V_Int, C4MenuWindowStyleFlag::FitChildren },
|
||||
{ "MENU_Multiple" ,C4V_Int, C4MenuWindowStyleFlag::Multiple },
|
||||
{ NULL, C4V_Nil, 0}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,8 @@
|
|||
#include <C4Surface.h>
|
||||
#include <C4Gui.h>
|
||||
|
||||
#include <C4Value.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
enum C4MenuWindowPropertyName
|
||||
|
@ -41,6 +43,10 @@ enum C4MenuWindowPropertyName
|
|||
symbolDef,
|
||||
text,
|
||||
onClickAction,
|
||||
onMouseInAction,
|
||||
onMouseOutAction,
|
||||
style,
|
||||
priority,
|
||||
_lastProp
|
||||
};
|
||||
|
||||
|
@ -50,6 +56,19 @@ enum C4MenuWindowActionID
|
|||
Call,
|
||||
};
|
||||
|
||||
enum C4MenuWindowStyleFlag
|
||||
{
|
||||
None = 0,
|
||||
Grid = 1,
|
||||
Vertical = 2,
|
||||
TextVCenter = 4,
|
||||
TextHCenter = 8,
|
||||
TextRight = 16,
|
||||
TextBottom = 32,
|
||||
FitChildren = 64,
|
||||
Multiple = 128,
|
||||
};
|
||||
|
||||
class C4MenuWindow;
|
||||
|
||||
class C4MenuWindowAction
|
||||
|
@ -57,18 +76,27 @@ class C4MenuWindowAction
|
|||
friend class C4MenuWindow;
|
||||
|
||||
private:
|
||||
// the ID is unique among all actions. It is used later to synchronize callbacks
|
||||
int32_t id;
|
||||
|
||||
int32_t action;
|
||||
C4MenuWindowAction *nextAction; // a linked list of actions
|
||||
// note: depending on the action not all of the following attributes always have values
|
||||
C4Object *target;
|
||||
C4PropList *target; // contains a valid C4Object in case of SetTag, a generic proplist in case of Call
|
||||
C4String *text; // can be either a function name to call or a tag to set
|
||||
C4Value value; // arbitrary value used for Call
|
||||
int32_t subwindowID;
|
||||
|
||||
public:
|
||||
C4MenuWindowAction() : action(0), target(0), text(0), subwindowID(0) { }
|
||||
C4MenuWindowAction() : id(0), action(0), nextAction(0), target(0), text(0), value(0), subwindowID(0) { }
|
||||
~C4MenuWindowAction();
|
||||
void ClearPointers(C4Object *pObj);
|
||||
void Execute(C4MenuWindow *parent);
|
||||
bool Init(C4ValueArray *array);
|
||||
bool Init(C4ValueArray *array, int32_t index = 0); // index is the current action in an array of actions
|
||||
// executes non-synced actions and syncs the others
|
||||
// the tag and action type parameters are only used to be able to sync commands
|
||||
void Execute(C4MenuWindow *parent, int32_t player, unsigned int tag, int32_t actionType);
|
||||
// used to execute synced commands, explanation see C4MenuWindow::ExecuteCommand
|
||||
bool ExecuteCommand(int32_t actionID, C4MenuWindow *parent, int32_t player);
|
||||
};
|
||||
|
||||
class C4MenuWindowProperty
|
||||
|
@ -89,6 +117,9 @@ class C4MenuWindowProperty
|
|||
} Prop;
|
||||
|
||||
Prop *current;
|
||||
// the last tag is used to be able to call the correct action on re-synchronizing commands
|
||||
unsigned int currentTag;
|
||||
|
||||
std::map<unsigned int, Prop> taggedProperties;
|
||||
void CleanUp(Prop &prop);
|
||||
void CleanUpAll();
|
||||
|
@ -101,7 +132,7 @@ class C4MenuWindowProperty
|
|||
|
||||
public:
|
||||
~C4MenuWindowProperty();
|
||||
C4MenuWindowProperty() : current(0), type(-1) {}
|
||||
C4MenuWindowProperty() : current(0), currentTag(0), type(-1) {}
|
||||
void Set(const C4Value &value, unsigned int hash);
|
||||
|
||||
int32_t GetInt() { return current->d; }
|
||||
|
@ -111,8 +142,10 @@ class C4MenuWindowProperty
|
|||
C4GUI::FrameDecoration *GetFrameDecoration() { return current->deco; }
|
||||
StdCopyStrBuf *GetStrBuf() { return current->strBuf; }
|
||||
C4MenuWindowAction *GetAction() { return current->action; }
|
||||
C4MenuWindowAction *GetActionForTag(unsigned int hash); // used to synchronize actions
|
||||
|
||||
void SwitchTag(C4String *tag);
|
||||
bool SwitchTag(C4String *tag);
|
||||
unsigned int GetCurrentTag() { return currentTag; }
|
||||
|
||||
void ClearPointers(C4Object *pObj);
|
||||
};
|
||||
|
@ -148,17 +181,33 @@ class C4MenuWindow
|
|||
|
||||
std::list<C4MenuWindow*> children;
|
||||
C4MenuWindow *parent;
|
||||
bool wasRemoved; // to notify the window that it should not inform its parent on Close() a second time
|
||||
|
||||
bool visible;
|
||||
C4Object *target;
|
||||
C4Object *GetTarget() { return target; }
|
||||
const C4Object *GetTarget() { return target; }
|
||||
C4MenuWindowScrollBar *scrollBar;
|
||||
|
||||
// this remembers whether the window currently has mouse focus
|
||||
// all windows with this property set are remembered by their parents and notified when the mouse left
|
||||
bool hasMouseFocus; // this needs to be saved in savegames!!!
|
||||
// OnMouseOut() called by this window, sets "hasMouseFocus" from true to false
|
||||
// must notify children, too!
|
||||
void OnMouseOut(int32_t player);
|
||||
void OnMouseIn(int32_t player); // called by this window, sets "hasMouseFocus" from false to true
|
||||
|
||||
// properties are stored extra to make "tags" possible
|
||||
C4MenuWindowProperty props[C4MenuWindowPropertyName::_lastProp];
|
||||
// used for sorting the windows in a layout
|
||||
static bool CompareMenuWindowsByPriority(C4MenuWindow *left, C4MenuWindow *right) { return left->props[C4MenuWindowPropertyName::priority].GetInt() < right->props[C4MenuWindowPropertyName::priority].GetInt(); };
|
||||
|
||||
void Init();
|
||||
void DrawChildren(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom);
|
||||
// withMultipleFlag is there to draw only the non-multiple or the multiple windows
|
||||
// withMultipleFlag == -1: all windows are drawn (standard)
|
||||
// withMultipleFlag == 0: only one non-Multiple window is drawn
|
||||
// withMultipleFlag == 1: only Multiple windows are drawn
|
||||
// returns whether at least one child was drawn
|
||||
bool DrawChildren(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom, int32_t withMultipleFlag = -1);
|
||||
// ID is set by parent, parent gives unique IDs to children
|
||||
void SetID(int32_t to) { id = to; }
|
||||
// to be used to generate the quick-access children map for main menus
|
||||
|
@ -171,8 +220,15 @@ class C4MenuWindow
|
|||
void SetArrayTupleProperty(const C4Value &property, C4MenuWindowPropertyName first, C4MenuWindowPropertyName second, unsigned int hash);
|
||||
|
||||
// this is only supposed to be called at ::MenuWindowRoot since it uses the "ID" property
|
||||
// this is done to make saving easier
|
||||
// this is done to make saving easier. Since IDs do not need to be sequental, action&menu IDs can both be derived from "id"
|
||||
int32_t GenerateMenuID() { return ++id; }
|
||||
int32_t GenerateActionID() { return ++id; }
|
||||
|
||||
void UpdateLayout();
|
||||
void UpdateLayoutGrid();
|
||||
void UpdateLayoutVertical();
|
||||
void EnableScrollBar(bool enable = true);
|
||||
|
||||
public:
|
||||
// used by mouse input, this is in screen coordinates
|
||||
struct _lastDrawPosition
|
||||
|
@ -211,16 +267,22 @@ class C4MenuWindow
|
|||
C4MenuWindow *AddChild(C4MenuWindow *child);
|
||||
C4MenuWindow *AddChild() { return AddChild(new C4MenuWindow()); }
|
||||
|
||||
void ClearChildren(bool close = true); // close: whether to properly "Close" them
|
||||
void RemoveChild(C4MenuWindow *child, bool close = true);
|
||||
void ClearChildren(bool close = true); // close: whether to properly "Close" them, alias for RemoveChild
|
||||
void RemoveChild(C4MenuWindow *child, bool close = true, bool all = false); // child = 0 & all = true to clear all
|
||||
void Close();
|
||||
void ClearPointers(C4Object *pObj);
|
||||
|
||||
// Draw without parameters can be used for the root
|
||||
void Draw(C4TargetFacet &cgo, int32_t player);
|
||||
void Draw(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom);
|
||||
bool Draw(C4TargetFacet &cgo, int32_t player);
|
||||
bool Draw(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom);
|
||||
bool GetClippingRect(float &left, float &top, float &right, float &bottom);
|
||||
virtual bool MouseInput(int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
|
||||
|
||||
// used for commands that have been synchronized and are coming from the command queue
|
||||
// the command is basically just a script that is executed, however, the plausibility is checked first
|
||||
// this plausibility check is the only reason CID_Script is not used!
|
||||
// attention: calls to this need to be synchronized!
|
||||
bool ExecuteCommand(int32_t actionID, int32_t player, int32_t subwindowID, int32_t actionType, C4Object *target, unsigned int tag);
|
||||
virtual bool MouseInput(int32_t player, int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
|
||||
};
|
||||
|
||||
extern C4MenuWindow MenuWindowRoot;
|
||||
|
|
|
@ -326,7 +326,7 @@ void C4MouseControl::Move(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyFl
|
|||
// are custom menus active?
|
||||
bool menuProcessed = false;
|
||||
if (pPlayer)
|
||||
menuProcessed = ::MenuWindowRoot.MouseInput(iButton, iX, iY, dwKeyFlags);
|
||||
menuProcessed = ::MenuWindowRoot.MouseInput(Player, iButton, iX, iY, dwKeyFlags);
|
||||
|
||||
// if not caught by a menu
|
||||
if (!menuProcessed)
|
||||
|
|
|
@ -123,6 +123,7 @@ const C4PktHandlingData PktHandlingData[] =
|
|||
{ CID_PlrControl, PC_Control, "Player Control", false, true, 0, PKT_UNPACK(C4ControlPlayerControl)},
|
||||
{ CID_PlrCommand, PC_Control, "Player Command", false, true, 0, PKT_UNPACK(C4ControlPlayerCommand)},
|
||||
{ CID_Message, PC_Control, "Message", false, true, 0, PKT_UNPACK(C4ControlMessage) },
|
||||
{ CID_MenuCommand, PC_Control, "Menu Command", false, true, 0, PKT_UNPACK(C4ControlMenuCommand)},
|
||||
{ CID_EMMoveObj, PC_Control, "EM Move Obj", false, true, 0, PKT_UNPACK(C4ControlEMMoveObject)},
|
||||
{ CID_EMDrawTool, PC_Control, "EM Draw Tool", false, true, 0, PKT_UNPACK(C4ControlEMDrawTool) },
|
||||
|
||||
|
|
|
@ -166,7 +166,8 @@ enum C4PacketType
|
|||
CID_EMMoveObj = CID_First | 0x30,
|
||||
CID_EMDrawTool = CID_First | 0x31,
|
||||
|
||||
CID_DebugRec = CID_First | 0x40
|
||||
CID_DebugRec = CID_First | 0x40,
|
||||
CID_MenuCommand = CID_First | 0x41,
|
||||
};
|
||||
|
||||
// packet classes
|
||||
|
|
|
@ -157,7 +157,10 @@ C4StringTable::C4StringTable()
|
|||
P[P_Std] = "Std";
|
||||
P[P_Text] = "Text";
|
||||
P[P_OnClick] = "OnClick";
|
||||
P[P_OnMouseIn] = "OnMouseIn";
|
||||
P[P_OnMouseOut] = "OnMouseOut";
|
||||
P[P_ID] = "ID";
|
||||
P[P_Style] = "Style";
|
||||
P[DFA_WALK] = "WALK";
|
||||
P[DFA_FLIGHT] = "FLIGHT";
|
||||
P[DFA_KNEEL] = "KNEEL";
|
||||
|
|
|
@ -362,6 +362,9 @@ enum C4PropertyName
|
|||
P_Text,
|
||||
P_ID,
|
||||
P_OnClick,
|
||||
P_OnMouseIn,
|
||||
P_OnMouseOut,
|
||||
P_Style,
|
||||
// Default Action Procedures
|
||||
DFA_WALK,
|
||||
DFA_FLIGHT,
|
||||
|
|
Loading…
Reference in New Issue