/** HUD Object Selector For each inventory item (and each vehicle, house etc on the same pos) one of this objects exists in the bottom bar. If clicked, the associated object is selected (inventory item is selected, vehicle is grabbed or ungrabbed, house is entered or exited...). HUD elements are passive, they don't update their status by themselves but rely on the clonk to update their status. This object works only for crew members that included the standard clonk controls (see Libraries.c4d/ClonkControl.c4d) @authors Newton */ /* usage of layers: ----------------- layer 0 - unused layer 1 - title layer 2 - actionicon layer 12 - hotkey */ local selected, crew, hotkey, myobject, actiontype, subselector, position, modus; local extra; // function to call and desc for extra-types static const ACTIONTYPE_INVENTORY = 0; static const ACTIONTYPE_VEHICLE = 1; static const ACTIONTYPE_STRUCTURE = 2; static const ACTIONTYPE_SCRIPT = 3; static const ACTIONTYPE_EXTRA = 4; private func HandSize() { return 400; } private func IconSize() { return 500; } protected func Construction() { _inherited(); selected = 0; hotkey = 0; position = 0; myobject = nil; subselector = nil; // parallaxity this.Parallaxity = [0,0]; // visibility this.Visibility = VIS_None; } protected func Destruction() { if(subselector) subselector->RemoveObject(); } public func ShowsItem() { if(myobject) return true; else return false; } public func MouseSelectionAlt(int plr) { if (!myobject) return; var desc = myobject.UsageHelp; if (!desc) desc = myobject.Description; // fall back to general description // close other messages... crew->OnDisplayInfoMessage(); if (desc) { var msg = Format("%s", desc); CustomMessage(msg, this, plr); } return true; } public func MouseSelection(int plr) { if(!crew) return false; if(plr != GetOwner()) return false; // no object if(!myobject) return false; // object is a pushable vehicle if(actiontype == ACTIONTYPE_VEHICLE) { var proc = crew->GetProcedure(); // object is inside building -> activate if(crew->Contained() && myobject->Contained() == crew->Contained()) { crew->SetCommand("Activate",myobject); return true; } // crew is currently pushing vehicle else if(proc == "PUSH") { // which is mine -> let go if(crew->GetActionTarget() == myobject) { // vehicles might have set their own view and it's not reset on release controls // So reset cursor view immediately when ungrabbing ResetCursorView(GetController()); crew->ObjectCommand("UnGrab"); } else crew->ObjectCommand("Grab", myobject); return true; } // grab else if(proc == "WALK") { crew->ObjectCommand("Grab", myobject); return true; } } // object is a building if(actiontype == ACTIONTYPE_STRUCTURE) { // inside? -> exit if(crew->Contained() == myobject) { crew->ObjectCommand("Exit"); return true; } // outside? -> enter else if(crew->CanEnter()) { crew->ObjectCommand("Enter", myobject); return true; } } if(actiontype == ACTIONTYPE_SCRIPT) { if(myobject->~IsInteractable(crew)) { myobject->Interact(crew, modus); return true; } } if(actiontype == ACTIONTYPE_EXTRA) { if(extra) extra.Object->Call(extra.Fn, crew); } } public func OnMouseDragDone(obj, object target) { // not on landscape if(target) return; if(GetType(obj) != C4V_C4Object) return; if(!crew) return false; var container; if(container = obj->Contained()) { if(obj->GetOCF() & OCF_Collectible) { container->SetCommand("Drop",obj); } else if(obj->GetOCF() & OCF_Grab) { if(crew->Contained() == container) crew->SetCommand("Activate",obj); } } } public func OnMouseDrag(int plr) { if(plr != GetOwner()) return false; if(actiontype == ACTIONTYPE_INVENTORY || actiontype == ACTIONTYPE_VEHICLE) return myobject; return false; } public func OnMouseDrop(int plr, obj) { if(plr != GetOwner()) return false; if(GetType(obj) != C4V_C4Object) return false; if(!crew) return false; // anything theoretically can be dropped on script objects if(actiontype == ACTIONTYPE_SCRIPT) { return myobject->~DropObject(obj); } // a collectible object else if(obj->GetOCF() & OCF_Collectible) { if(actiontype == ACTIONTYPE_INVENTORY) { var objcontainer = obj->Contained(); // object container is the clonk too? Just switch if(objcontainer == crew) { crew->Switch2Items(position, crew->GetItemPos(obj)); return true; } // slot is already full: switch places with other object if(myobject != nil) { var myoldobject = myobject; // 1. exit my object myoldobject->Exit(); // 2. try to enter the other object if(crew->Collect(obj,false,position)) { // 3. try to enter my object into other container if(!(objcontainer->Collect(myoldobject,true))) // -> otherwise, recollect my object crew->Collect(myoldobject,false,position); } // -> otherwise, recollect my object else crew->Collect(myoldobject,false,position); } // otherwise, just collect else { crew->Collect(obj,false,position); } return true; } else if(actiontype == ACTIONTYPE_VEHICLE) { if(!myobject) return false; // collect if possible if(myobject->Collect(obj)) return true; // otherwise (lorry is full?): fail return false; } } else if(obj->GetOCF() & OCF_Grab) { if(actiontype == ACTIONTYPE_STRUCTURE) { // respect no push enter if (obj->GetDefCoreVal("NoPushEnter","DefCore")) return false; // enter vehicle into structure crew->ObjectCommand("PushTo", obj, 0, 0, myobject); return true; } } } public func Disable() { myobject = nil; actiontype = -1; hotkey = 0; position = 0; this["Visibility"] = VIS_None; if(subselector) subselector->RemoveObject(); } public func ClearMessage() { CustomMessage("",this,GetOwner()); if(subselector) CustomMessage("",subselector,GetOwner()); } public func SetObject(object obj, int type, int pos, int hot, int number, proplist xtra) { this.Visibility = VIS_Owner; // remove effect that checks whether the object to which this selector // refers to is still existant because now this selector gets a new // object to which to refer. RemoveEffect("IntRemoveGuard",myobject); position = pos; actiontype = type; myobject = obj; hotkey = hot; modus = number; // extra stuff if(xtra) extra = xtra; else extra = nil; // Set mousedrag for inventory objects if (actiontype == ACTIONTYPE_INVENTORY) this.MouseDrag = MD_DragSource | MD_DropTarget; if(!myobject) { SetGraphics("None"); SetGraphics(nil,nil,1); SetName(Format("$TxtSlot$",pos+1)); this.MouseDragImage = nil; if(subselector) subselector->RemoveObject(); } else { SetGraphics("Slot", GUI_ObjectSelector); SetGraphics(nil,nil,1,GFXOV_MODE_ObjectPicture, nil, 0, myobject); SetName(Format("$TxtSelect$",myobject->GetName())); this.MouseDragImage = myobject; // if object has extra slot, show it if(myobject->~HasExtraSlot()) { if(!subselector) { subselector = CreateObjectAbove(GUI_ExtraSlot,0,0,GetOwner()); subselector->SetPosition(GetX()+24,GetY()+24); } subselector->SetContainer(myobject); subselector->SetCrew(crew); } // or otherwise, remove it else if(subselector) { subselector->RemoveObject(); } // create an effect which monitors whether the object is removed: // this is only necessary for inventory as all the other slots // are checked via the effect if(actiontype == ACTIONTYPE_INVENTORY) { AddEffect("IntRemoveGuard",myobject,1,0,this); } } ShowHotkey(); UpdateSelectionStatus(); } public func ShowSelector() { this["Visibility"] = VIS_Owner; } public func HideSelector() { this["Visibility"] = VIS_None; } public func FxIntRemoveGuardStop(object target, effect, int reason, bool temp) { if(reason == 3) if(target == myobject) SetObject(nil,0,position); } public func SetCrew(object c) { if(crew == c) return; crew = c; SetOwner(c->GetOwner()); this["Visibility"] = VIS_Owner; } public func GetCrew() { return crew; } public func ShowHotkey() { if(hotkey > 10 || hotkey <= 0) { SetGraphics(nil,nil,12); } else { var num = hotkey; if(hotkey == 10) num = 0; var name = Format("%d",num); SetGraphics(name,Icon_Number,12,GFXOV_MODE_IngamePicture); SetObjDrawTransform(300,0,14500,0,300,-32000, 12); SetClrModulation(RGB(255,230,180),12); } } public func Selected() { return selected; } public func UpdateSelectionStatus() { if(!crew) return; // determine... var sel = 0; if(actiontype == ACTIONTYPE_EXTRA) { if(extra) { SetGraphics(extra.IconName,extra.IconID,2,GFXOV_MODE_IngamePicture); this.Tooltip = extra.Description; } } // script... else if(actiontype == ACTIONTYPE_SCRIPT) { this.Tooltip = nil; var metainfo = myobject->~GetInteractionMetaInfo(crew, modus); if(metainfo) { SetGraphics(metainfo["IconName"],metainfo["IconID"],2,GFXOV_MODE_IngamePicture); SetObjDrawTransform(IconSize(),0,-16000,0,IconSize(),20000, 2); var desc = metainfo["Description"]; if(desc) this.Tooltip = desc; if(metainfo["Selected"]) SetObjDrawTransform(1200,0,0,0,1200,0,1); } return; } else if(actiontype == ACTIONTYPE_VEHICLE) { if(crew->GetProcedure() == "PUSH" && crew->GetActionTarget() == myobject) sel = 1; } else if(actiontype == ACTIONTYPE_STRUCTURE) { if(crew->Contained() == myobject) sel = 1; } else if(actiontype == ACTIONTYPE_INVENTORY) { if(0 == position) sel += 1; if(1 == position) sel += 2; } selected = sel; // and set the icon... if(selected) { //SetClrModulation(RGB(220,0,0),12); //SetObjDrawTransform(500,0,16000,0,500,-34000, 12); if(myobject) { SetObjDrawTransform(1200,0,0,0,1200,0,1); } if(actiontype == ACTIONTYPE_VEHICLE) { SetGraphics("LetGo",GetID(),2,GFXOV_MODE_Base); this.Tooltip = Format("$TxtUnGrab$",myobject->GetName()); } else if(actiontype == ACTIONTYPE_STRUCTURE) { SetGraphics("Exit",GetID(),2,GFXOV_MODE_Base); this.Tooltip = Format("$TxtExit$",myobject->GetName()); } } else { //SetClrModulation(RGB(160,0,0),12); //SetObjDrawTransform(300,0,16000,0,300,-34000, 12); if(myobject) { SetObjDrawTransform(900,0,0,0,900,0,1); } if(actiontype == ACTIONTYPE_VEHICLE) { if(!(myobject->Contained())) { SetGraphics("Grab",GetID(),2,GFXOV_MODE_Base); this.Tooltip = Format("$TxtGrab$",myobject->GetName()); } else { SetGraphics("Exit",GetID(),2,GFXOV_MODE_Base); this.Tooltip = Format("$TxtPushOut$",myobject->GetName()); } } if(actiontype == ACTIONTYPE_STRUCTURE) { SetGraphics("Enter",GetID(),2,GFXOV_MODE_Base); this.Tooltip = Format("$TxtEnter$",myobject->GetName()); } } SetObjDrawTransform(IconSize(),0,-16000,0,IconSize(),20000, 2); }