|  | @@ -19,17 +19,62 @@
 | 
	
		
			
				|  |  |  #include "la_5.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  extern LA MAIN;
 | 
	
		
			
				|  |  | +extern tnsMain* T;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int la_GetKeyablePropertyStorageSize(laProp* p);
 | 
	
		
			
				|  |  |  void la_AnimationEvaluateActions(int ClampOffsets);
 | 
	
		
			
				|  |  | +void laget_AnimationActionHolderCategory(void* a_unused, laActionHolder* ah, char* copy, char** ptr);
 | 
	
		
			
				|  |  | +void laui_AnimationActionHolder(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *ExtraColumns, int context);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int laAnimationRegisterHolderPath(const char* Path){
 | 
	
		
			
				|  |  | +    laPropPack PP={0}; if(!la_GetPropFromPath(&PP,0,Path,0)){ la_FreePropStepCache(PP.Go); return 0; }
 | 
	
		
			
				|  |  | +    la_EnsureSubTarget(PP.LastPs->p,0);
 | 
	
		
			
				|  |  | +    if((!PP.LastPs->p->SubProp) || (!la_PropLookup(&PP.LastPs->p->SubProp->Props,"__actions__"))){
 | 
	
		
			
				|  |  | +        la_FreePropStepCache(PP.Go); return 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    laActionHolderPath* ahp=memAcquire(sizeof(laActionHolderPath));
 | 
	
		
			
				|  |  | +    ahp->OriginalPath=Path;
 | 
	
		
			
				|  |  | +    la_CopyPropPack(&PP,&ahp->PP); lstAppendItem(&MAIN.Animation->ActionHolderPaths,ahp);
 | 
	
		
			
				|  |  | +    la_FreePropStepCache(PP.Go);
 | 
	
		
			
				|  |  | +    return 1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void laAnimationUpdateHolderList(){
 | 
	
		
			
				|  |  | +    laActionHolder* ah;
 | 
	
		
			
				|  |  | +    while(ah=lstPopItem(&MAIN.Animation->ActionHolders)){
 | 
	
		
			
				|  |  | +        strSafeDestroy(&ah->Name); strSafeDestroy(&ah->CategoryTitle); memFree(ah);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for(laActionHolderPath* ahp=MAIN.Animation->ActionHolderPaths.pFirst;ahp;ahp=ahp->Item.pNext){
 | 
	
		
			
				|  |  | +        la_StepPropPack(&ahp->PP); laSubProp* pa=ahp->PP.LastPs->p; la_EnsureSubTarget(pa,0); laPropIterator pi={0};
 | 
	
		
			
				|  |  | +        int ListOffset=0; laSubProp* paa=la_PropLookup(&pa->Base.SubProp->Props,"__actions__");
 | 
	
		
			
				|  |  | +        if((!paa) || (!paa->ListHandleOffset)){ continue; } ListOffset=paa->ListHandleOffset;
 | 
	
		
			
				|  |  | +        void* inst=laGetInstance(ahp->PP.LastPs->p,ahp->PP.LastPs->UseInstance,&pi); int FirstIn=1;
 | 
	
		
			
				|  |  | +        while(inst){
 | 
	
		
			
				|  |  | +            ah=memAcquire(sizeof(laActionHolder));
 | 
	
		
			
				|  |  | +            memAssignRef(ah,&ah->Instance,inst);
 | 
	
		
			
				|  |  | +            ah->ListHandleOffset=ListOffset;
 | 
	
		
			
				|  |  | +            char _id[64]="unamed", *id=_id;
 | 
	
		
			
				|  |  | +            laTryGetInstanceIdentifier(inst,pa->Base.SubProp,_id,&id);
 | 
	
		
			
				|  |  | +            strSafeSet(&ah->Name,id);
 | 
	
		
			
				|  |  | +            if(FirstIn){ strSafeSet(&ah->CategoryTitle,pa->Base.SubProp->Name); }
 | 
	
		
			
				|  |  | +            lstAppendItem(&MAIN.Animation->ActionHolders,ah);
 | 
	
		
			
				|  |  | +            inst=laGetNextInstance(ahp->PP.LastPs->p, inst, &pi);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    laNotifyUsers("la.animation.action_holders"); 
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -laAction* laAnimiationNewAction(char* Name){
 | 
	
		
			
				|  |  | +laAction* laAnimiationNewAction(laActionHolder* ah, char* Name){
 | 
	
		
			
				|  |  |      laAction* aa=memAcquire(sizeof(laAction));
 | 
	
		
			
				|  |  |      if(!Name || !Name[0]){ Name="New Action"; }
 | 
	
		
			
				|  |  |      strSafeSet(&aa->Name,Name);
 | 
	
		
			
				|  |  |      aa->Length=2; aa->FrameCount=24;
 | 
	
		
			
				|  |  |      memAssignRef(MAIN.Animation,&MAIN.Animation->CurrentAction,aa);
 | 
	
		
			
				|  |  | -    lstAppendItem(&MAIN.Animation->Actions,aa); laNotifyUsers("la.animation.actions");
 | 
	
		
			
				|  |  | +    void* lh=((uint8_t*)ah->Instance)+ah->ListHandleOffset;
 | 
	
		
			
				|  |  | +    lstAppendItem(lh,aa);
 | 
	
		
			
				|  |  | +    laNotifyInstanceUsers(ah->Instance);
 | 
	
		
			
				|  |  | +    laNotifyUsers("la.animation.current_action");
 | 
	
		
			
				|  |  |      return aa;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  laActionProp* laAnimationEnsureProp(void* hyper1, laProp* p){
 | 
	
	
		
			
				|  | @@ -95,11 +140,55 @@ void laAnimationSetPlayHead(real time){
 | 
	
		
			
				|  |  |      MAIN.Animation->PlayHead=time;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +STRUCTURE(laNewActionData){
 | 
	
		
			
				|  |  | +    laActionHolder* SelectedHolder;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +laActionHolder* laget_AnimationFirstActionHolder(void* unused1, void* unused2){
 | 
	
		
			
				|  |  | +    return MAIN.Animation->ActionHolders.pFirst;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laset_AnimationNewActionSetHolder(laNewActionData *np, laActionHolder *ah, int State){
 | 
	
		
			
				|  |  | +    np->SelectedHolder = ah;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  int OPINV_AnimationNewAction(laOperator *a, laEvent *e){
 | 
	
		
			
				|  |  | -    laAnimiationNewAction(0);
 | 
	
		
			
				|  |  | -    return LA_FINISHED;
 | 
	
		
			
				|  |  | +    laAnimationUpdateHolderList();
 | 
	
		
			
				|  |  | +    laNewActionData* np= CreateNew(laNewActionData);
 | 
	
		
			
				|  |  | +    a->CustomData = np;
 | 
	
		
			
				|  |  | +    laEnableOperatorPanel(a, 0, e->x-50,e->y-50,500,500,10000,0,0,0,0,0,0,0,e);
 | 
	
		
			
				|  |  | +    return LA_RUNNING;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +int OPMOD_AnimationNewAction(laOperator *a, laEvent *e){
 | 
	
		
			
				|  |  | +    laNewActionData* np=a->CustomData;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if(!a->ConfirmData) return LA_RUNNING;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if(a->ConfirmData->Mode == LA_CONFIRM_CANCEL||a->ConfirmData->Mode == LA_CONFIRM_OK){ if(np) free(np); return LA_CANCELED; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if(a->ConfirmData->Mode == LA_CONFIRM_DATA){
 | 
	
		
			
				|  |  | +        if (!np || !np->SelectedHolder){ if(np) free(np); return LA_CANCELED; }
 | 
	
		
			
				|  |  | +        laAnimiationNewAction(np->SelectedHolder,0); free(np);
 | 
	
		
			
				|  |  | +        return LA_FINISHED;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return LA_RUNNING;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +void laui_AnimationNewAction(laUiList *uil, laPropPack *This, laPropPack *OperatorProps, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | +    laColumn *c = laFirstColumn(uil);
 | 
	
		
			
				|  |  | +    laUiItem* ui=laShowItem(uil, c, OperatorProps, "holder");ui->SymbolID=3; ui->Flags|=LA_UI_FLAGS_NO_DECAL;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int OPINV_AnimationSelectAction(laOperator *a, laEvent *e){
 | 
	
		
			
				|  |  | +    laEnableOperatorPanel(a, 0, e->x-50,e->y-50,500,500,10000,0,0,0,0,0,0,0,e);
 | 
	
		
			
				|  |  | +    return LA_RUNNING;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laui_AnimationSelectAction(laUiList *uil, laPropPack *This, laPropPack *OperatorProps, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | +    laColumn *c = laFirstColumn(uil);
 | 
	
		
			
				|  |  | +    for(laActionHolderPath* ahp=MAIN.Animation->ActionHolderPaths.pFirst;ahp;ahp=ahp->Item.pNext){
 | 
	
		
			
				|  |  | +        laShowLabel(uil,c,ahp->PP.LastPs->p->Name,0,0)->Flags|=LA_UI_FLAGS_DISABLED|LA_TEXT_MONO;
 | 
	
		
			
				|  |  | +        laShowItemFull(uil,c,0,ahp->OriginalPath,LA_WIDGET_COLLECTION,0,laui_AnimationActionHolder,0)->Flags|=LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_GAP;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int OPINV_AnimationPlayAction(laOperator *a, laEvent *e){
 | 
	
		
			
				|  |  |      char* str=strGetArgumentString(a->ExtraInstructionsP, "mode");
 | 
	
		
			
				|  |  |      int PlayStatus=LA_ANIMATION_STATUS_PAUSED;
 | 
	
	
		
			
				|  | @@ -192,23 +281,28 @@ void la_AnimationEvaluateActionChannels(laAction* aa){
 | 
	
		
			
				|  |  |  void la_AnimationEvaluateActions(int ClampOffsets){
 | 
	
		
			
				|  |  |      int any=0;
 | 
	
		
			
				|  |  |      la_AnimationMarkPropReset();
 | 
	
		
			
				|  |  | -    for(laAction* aa=MAIN.Animation->Actions.pFirst;aa;aa=aa->Item.pNext){
 | 
	
		
			
				|  |  | -        real preoffset=0,postoffset=aa->Offset/aa->Length;
 | 
	
		
			
				|  |  | -        if(ClampOffsets || (MAIN.Animation->PlayStatus!=LA_ANIMATION_STATUS_PAUSED)){
 | 
	
		
			
				|  |  | -            while(aa->Offset>aa->Length){ aa->Offset-=aa->Length; }
 | 
	
		
			
				|  |  | -            while(aa->Offset<-1e-6){ aa->Offset+=aa->Length; }
 | 
	
		
			
				|  |  | -            preoffset=aa->Offset; postoffset=0;
 | 
	
		
			
				|  |  | +    for(laActionHolder* ah=MAIN.Animation->ActionHolders.pFirst;ah;ah=ah->Item.pNext){
 | 
	
		
			
				|  |  | +        if(!ah->Instance) continue;
 | 
	
		
			
				|  |  | +        laListHandle* lh=((uint8_t*)ah->Instance)+ah->ListHandleOffset;
 | 
	
		
			
				|  |  | +        for(laAction* aa=lh->pFirst;aa;aa=aa->Item.pNext){
 | 
	
		
			
				|  |  | +            real preoffset=0,postoffset=aa->Offset/aa->Length;
 | 
	
		
			
				|  |  | +            if(ClampOffsets || (MAIN.Animation->PlayStatus!=LA_ANIMATION_STATUS_PAUSED)){
 | 
	
		
			
				|  |  | +                while(aa->Offset>aa->Length){ aa->Offset-=aa->Length; }
 | 
	
		
			
				|  |  | +                while(aa->Offset<-1e-6){ aa->Offset+=aa->Length; }
 | 
	
		
			
				|  |  | +                preoffset=aa->Offset; postoffset=0;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            real UseTime=MAIN.Animation->PlayHead-preoffset;
 | 
	
		
			
				|  |  | +            int repeats=UseTime/aa->Length;
 | 
	
		
			
				|  |  | +            real remaining=UseTime-repeats*aa->Length;
 | 
	
		
			
				|  |  | +            while(remaining<0){ remaining+=aa->Length; }
 | 
	
		
			
				|  |  | +            if(aa->PlayMode==LA_ANIMATION_PLAY_MODE_REPEAT){ aa->PlayHead=remaining/aa->Length-postoffset; }
 | 
	
		
			
				|  |  | +            elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_HOLD){ aa->PlayHead=((UseTime>aa->Length)?1.0:(UseTime<0?0:UseTime/aa->Length))-postoffset; }
 | 
	
		
			
				|  |  | +            elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_BOUNCE){ real t=remaining/aa->Length; aa->PlayHead=((repeats%2)?(1-t):t)-postoffset; }
 | 
	
		
			
				|  |  | +            any=1;
 | 
	
		
			
				|  |  | +            la_AnimationEvaluateActionChannels(aa);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        real UseTime=MAIN.Animation->PlayHead-preoffset;
 | 
	
		
			
				|  |  | -        int repeats=UseTime/aa->Length;
 | 
	
		
			
				|  |  | -        real remaining=UseTime-repeats*aa->Length;
 | 
	
		
			
				|  |  | -        while(remaining<0){ remaining+=aa->Length; }
 | 
	
		
			
				|  |  | -        if(aa->PlayMode==LA_ANIMATION_PLAY_MODE_REPEAT){ aa->PlayHead=remaining/aa->Length-postoffset; }
 | 
	
		
			
				|  |  | -        elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_HOLD){ aa->PlayHead=((UseTime>aa->Length)?1.0:(UseTime<0?0:UseTime/aa->Length))-postoffset; }
 | 
	
		
			
				|  |  | -        elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_BOUNCE){ real t=remaining/aa->Length; aa->PlayHead=((repeats%2)?(1-t):t)-postoffset; }
 | 
	
		
			
				|  |  | -        any=1;
 | 
	
		
			
				|  |  | -        la_AnimationEvaluateActionChannels(aa);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  |      for(laActionProp* ap=MAIN.Animation->Props.pFirst;ap;ap=ap->Item.pNext){ if(ap->Reset){ continue; }
 | 
	
		
			
				|  |  |          la_AnimationSetPropValue(ap);
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -548,7 +642,16 @@ void la_AnimationActionDrawOverlay(laUiItem *ui, int h){
 | 
	
		
			
				|  |  |  void la_RegisterAnimationResources(){
 | 
	
		
			
				|  |  |      laPropContainer *pc; laProp *p; laOperatorType *at; laEnumProp *ep;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    laCreateOperatorType("LA_animation_new_action", "New Action", "Add a new action",0,0,0,OPINV_AnimationNewAction,0,U'🞦',0);
 | 
	
		
			
				|  |  | +    at=laCreateOperatorType("LA_animation_new_action", "New Action", "Add a new action",0,0,0,OPINV_AnimationNewAction,OPMOD_AnimationNewAction,U'🞦',0);
 | 
	
		
			
				|  |  | +    pc = laDefineOperatorProps(at, 0);
 | 
	
		
			
				|  |  | +    p = laAddSubGroup(pc, "holder", "Holder", "Action holder to add the new action into", "la_animation_action_holder",
 | 
	
		
			
				|  |  | +        0, 0, laui_IdentifierOnly, -1, laget_AnimationFirstActionHolder, 0, laget_ListNext, 0, 0, laset_AnimationNewActionSetHolder,0,0);
 | 
	
		
			
				|  |  | +    laSubGroupExtraFunctions(p,0,0,0,laget_AnimationActionHolderCategory);
 | 
	
		
			
				|  |  | +    at->UiDefine=laui_AnimationNewAction;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    at=laCreateOperatorType("LA_animation_select_action", "Select Action", "Select an action",0,0,0,OPINV_AnimationSelectAction,OPMOD_FinishOnData,U'⯆',0);
 | 
	
		
			
				|  |  | +    at->UiDefine=laui_AnimationSelectAction;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_animation_set_play_status", "Set Play", "Set global animation player status",0,0,0,OPINV_AnimationPlayAction,0,0,0);
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_animation_reset_time", "Reset Time", "Reset Time",0,0,0,OPINV_AnimationResetTime,0,U'🡄',0);
 | 
	
		
			
				|  |  |  
 |