1
0
KF2-Dev-Scripts/GameFramework/Classes/MobileMenuInventory.uc
2020-12-13 18:01:13 +03:00

379 lines
9.8 KiB
Ucode

/**
* MobileMenuInventory
* A container of objects that can be scrolled through.
*
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
*/
class MobileMenuInventory extends MobileMenuObject;
struct RenderElementInfo
{
var bool bIsDragItem;
var int Index;
};
struct DragElementInfo
{
var bool bIsDragging;
var int IndexFrom;
var bool bIsOver;
var int IndexOver;
var bool bCanDropInOver;
var Vector2D OrigTouch;
var Vector2D CurTouch;
var ETouchType EventType;
};
/** Locations to put items */
var array<MobileMenuElement> Slots;
/** What items are in inventory - the index matches with what slot it is in */
var array<MobileMenuElement> Items;
/** Extra amount of distance on each side to detect touch */
var float SideLeewayPercent;
/** Info about the cur element being rendered. */
var RenderElementInfo CurrentElement;
/** Data about what is being drug by the finger - if any */
var DragElementInfo Drag;
/** How this was scaled if any other things are added */
var Vector2D ScaleSize;
/** If set to false, user should call RenderDragItem() after scene is drawn.*/
var bool bRenderDragItem;
/* Rather than inheriting, a class can use delegates to allow an item in a slot and be updated when it happens */
delegate OnUpdateItemInSlot(MobileMenuInventory FromInv, int SlotIndex);
delegate bool DoCanPutItemInSlot(MobileMenuInventory FromInv, MobileMenuElement Item, MobileMenuElement ToSlot, int ToIdx, int FromIdx);
delegate OnUpdateDrag(out const DragElementInfo Before, out const DragElementInfo After);
/**
* InitMenuObject - Virtual override from base to init object.
*
* @param PlayerInput - A pointer to the MobilePlayerInput object that owns the UI system
* @param Scene - The scene this object is in
* @param ScreenWidth - The Width of the Screen
* @param ScreenHeight - The Height of the Screen
*/
function InitMenuObject(MobilePlayerInput PlayerInput, MobileMenuScene Scene, int ScreenWidth, int ScreenHeight, bool bIsFirstInitialization)
{
local MobileMenuElement Element;
// Save before they are modified in InitMenuObject.
ScaleSize.X = Width;
ScaleSize.Y = Height;
Super.InitMenuObject(PlayerInput, Scene, ScreenWidth, ScreenHeight, bIsFirstInitialization);
// Now see how they were scaled and scale all Slot locations the same way.
ScaleSize.X = Width/ScaleSize.X;
ScaleSize.Y = Height/ScaleSize.Y;
foreach Slots(Element)
{
ScaleSlot(Element);
}
foreach Items(Element)
{
ScaleSlot(Element);
}
Items.Length = Slots.Length;
}
/** Add slots to the inventory system - return index */
function int AddSlot(MobileMenuElement Slot)
{
if (Slot != none)
{
Slots.AddItem(Slot);
if (bHasBeenInitialized)
{
ScaleSlot(Slot);
}
return Slots.Length - 1;
}
return -1;
}
/** Scale slot in same way this object was scaled - use same scaling this MobileMenuObject uses */
private function ScaleSlot(MobileMenuElement Slot)
{
Slot.VpPos.X *= ScaleSize.X;
Slot.VpPos.Y *= ScaleSize.Y;
Slot.VpSize.X *= ScaleSize.X;
Slot.VpSize.Y *= ScaleSize.Y;
}
/** Inheriting class will need to override this function if there are restrictions on what slot
* an element can be placed.
* Owning class can set the DoCanPutItemInSlot().
*
* @param Item - Item that wants to go into slot
* @param ToSlot - Slot to receive element
* @param ToIdx - Index of Slot - where we want to put element.
* @param FromIdx - Index of slot element is currently in - if any
*/
function bool CanPutItemInSlot(MobileMenuElement Item, MobileMenuElement ToSlot, int ToIdx, int FromIdx=-1)
{
if ((Item == none) || (FromIdx == ToIdx) || (ToIdx < 0))
return false;
if (DoCanPutItemInSlot != none)
{
return DoCanPutItemInSlot(self, Item, ToSlot, ToIdx, FromIdx);
}
return true;
}
/**
* This event is called when a "touch" event is detected on the object.
* If false is returned (unhanded) event will be passed to scene.
*
* @param EventType - type of event
* @param TouchX - The X location of the touch event
* @param TouchY - The Y location of the touch event
* @param ObjectOver - The Object that mouse is over (NOTE: May be NULL or another object!)
*/
event bool OnTouch(ETouchType EventType, float TouchX, float TouchY, MobileMenuObject ObjectOver, float DeltaTime)
{
local DragElementInfo OrigDrag;
OrigDrag = Drag;
Drag.EventType = EventType;
TouchX -= Left;
TouchY -= Top;
Drag.CurTouch.X = TouchX;
Drag.CurTouch.Y = TouchY;
switch (EventType)
{
case Touch_Began:
InitDragAt(TouchX, TouchY);
if (OnUpdateDrag != none)
OnUpdateDrag(OrigDrag, Drag);
return true;
case Touch_Moved:
case Touch_Stationary:
if (!Drag.bIsDragging)
{
InitDragAt(TouchX, TouchY);
}
else
{
Drag.IndexOver = FindSlotIndexAt(TouchX, TouchY);
Drag.bIsOver = Drag.IndexOver >= 0;
}
Drag.bCanDropInOver = Drag.bIsOver && CanPutItemInSlot(Items[Drag.IndexFrom], Slots[Drag.IndexOver], Drag.IndexOver, Drag.IndexFrom);
if (OnUpdateDrag != none)
OnUpdateDrag(OrigDrag, Drag);
return true;
case Touch_Ended:
if (Drag.bIsDragging)
{
// If we were not over something, try one last time,
// but do not remove it because sometimes when people lift they move too much.
if (!Drag.bIsOver)
{
Drag.IndexOver = FindSlotIndexAt(TouchX, TouchY);
Drag.bIsOver = Drag.IndexOver >= 0;
}
Drag.bCanDropInOver = Drag.bIsOver && CanPutItemInSlot(Items[Drag.IndexFrom], Slots[Drag.IndexOver], Drag.IndexOver, Drag.IndexFrom);
if (Drag.bCanDropInOver)
{
SwapItemsInSlots(Drag.IndexOver, Drag.IndexFrom);
}
}
break;
case Touch_Cancelled:
break;
}
// Only come here where we are done.
Drag.bIsDragging = false;
if (OnUpdateDrag != none)
OnUpdateDrag(OrigDrag, Drag);
// Don't change until after the OnUpdateDrag() because it will want to know last state.
Drag.bCanDropInOver = false;
Drag.bIsOver = false;
return true;
}
function bool SwapItemsInSlots(int Slot0, int Slot1)
{
local MobileMenuElement Element0, Element1;
Element0 = Items[Slot0];
Element1 = Items[Slot1];
if ((Element0 == none) || CanPutItemInSlot(Element0, Slots[Slot1], Slot1, Slot0))
{
if ((Element1 == none) || CanPutItemInSlot(Element1, Slots[Slot0], Slot0, Slot1))
{
Items[Slot0] = Element1;
Items[Slot1] = Element0;
UpdateItemInSlot(Slot0);
UpdateItemInSlot(Slot1);
return true;
}
}
return false;
}
function MobileMenuElement AddItemToSlot(MobileMenuElement Element, int ToSlot)
{
local MobileMenuElement PrevElement;
if (CanPutItemInSlot(Element, Slots[ToSlot], ToSlot))
{
PrevElement = Items[ToSlot];
Items[ToSlot] = Element;
UpdateItemInSlot(ToSlot);
return PrevElement;
}
return none;
}
protected function UpdateItemInSlot(int InSlot)
{
local MobileMenuElement Element, Slot;
Element = Items[InSlot];
if (Element != none)
{
Slot = Slots[InSlot];
Element.VpPos = Slot.VpPos;
Element.VpSize = Slot.VpSize;
}
if (OnUpdateItemInSlot != none)
{
OnUpdateItemInSlot(self, InSlot);
}
}
function InitDragAt(int TouchX, int TouchY)
{
Drag.IndexFrom = FindSlotIndexAt(TouchX, TouchY);
Drag.bIsDragging = (Drag.IndexFrom >= 0) && (Items[Drag.IndexFrom] != none);
Drag.IndexOver = Drag.IndexFrom;
Drag.bIsOver = (Drag.IndexFrom >= 0);
Drag.bCanDropInOver = false;
Drag.OrigTouch.X = TouchX;
Drag.OrigTouch.Y = TouchY;
}
function int FindSlotIndexAt(float X, float Y)
{
local MobileMenuElement Element;
local float ExtraX, ExtraY;
local int Idx;
Idx = -1;
foreach Slots(Element)
{
Idx++;
if (Element.bIsActive)
{
ExtraX = Element.VpSize.X * SideLeewayPercent;
ExtraY = Element.VpSize.Y * SideLeewayPercent;
if (X < (Element.VpPos.X - ExtraX))
continue;
if (Y < (Element.VpPos.Y - ExtraY))
continue;
if (X > ((Element.VpPos.X + Element.VpSize.X) + ExtraX))
continue;
if (Y > ((Element.VpPos.Y + Element.VpSize.Y) + ExtraY))
continue;
return Idx;
}
}
return -1;
}
function int GetIndexOfItem(MobileMenuElement Item)
{
return Items.Find(Item);
}
/**
* Render the widget
*
* @param Canvas - the canvas object for drawing
*/
function RenderObject(canvas Canvas, float DeltaTime)
{
local MobileMenuElement Element;
local float OrgX, OrgY;
OrgX = Canvas.OrgX;
OrgY = Canvas.OrgY;
CurrentElement.bIsDragItem = false;
CurrentElement.Index = 0;
foreach Slots(Element)
{
if (Element.bIsVisible)
{
Canvas.SetOrigin(Left + Element.VpPos.X, Top + Element.VpPos.Y);
Element.RenderElement(self, Canvas, DeltaTime, Opacity);
}
CurrentElement.Index++;
}
// ForEach does not work because it does not return null slots, therefore CurrentElement.Index cannot be calculated.
for (CurrentElement.Index = 0; CurrentElement.Index < Items.Length; CurrentElement.Index++)
{
Element = Items[CurrentElement.Index];
if (Element != none && Element.bIsVisible)
{
Canvas.SetOrigin(Left + Element.VpPos.X, Top + Element.VpPos.Y);
Element.RenderElement(self, Canvas, DeltaTime, Opacity);
}
}
// Restore to not mess up next scene.
Canvas.OrgX = OrgX;
Canvas.OrgY = OrgY;
if (bRenderDragItem)
RenderDragItem(Canvas, DeltaTime);
}
function RenderDragItem(canvas Canvas, float DeltaTime)
{
local MobileMenuElement Element;
local float OrgX, OrgY;
if (Drag.bIsDragging)
{
OrgX = Canvas.OrgX;
OrgY = Canvas.OrgY;
CurrentElement.bIsDragItem = true;
CurrentElement.Index = Drag.IndexFrom;
Element = Items[Drag.IndexFrom];
Canvas.SetOrigin(Left + Drag.CurTouch.X, Top + Drag.CurTouch.Y);
Element.RenderElement(self, Canvas, DeltaTime, Opacity);
// Restore to not mess up next scene.
Canvas.OrgX = OrgX;
Canvas.OrgY = OrgY;
}
}
defaultproperties
{
bIsActive=true
bRenderDragItem=true
SideLeewayPercent=0.1f
}