#include "vn_scene_commands.cpp" per_thread scene_view *ThreadLocal_SceneView = 0; static void SV_SetState(scene_view *View) { ThreadLocal_SceneView = View; } static scene_view *SV_GetState() { return(ThreadLocal_SceneView); } static void SV_NewFrame(scene_view *View, platform_event_list *EventList, r32 dtForFrame) { SV_SetState(View); View->EventList = EventList; View->dtForFrame = dtForFrame; } static void SV_Reset(void) { scene_view *SceneView = SV_GetState(); // sixten: reset onscreen characters SceneView->CharactersUsed = 0; } static void SV_LoadNavItems(string FileName) { scene_view *SceneView = SV_GetState(); temp Scratch = GetScratch(); string NavData = Platform_ReadEntireFile(Scratch.Arena, FileName); if(NavData.Count != 0) { u8 *Byte = NavData.Data; SceneView->NavItemCount = *(u16 *)Byte; Byte += 2; SceneView->NavItems = PushArrayNoClear(SceneView->SceneArena, scene_nav_item, SceneView->NavItemCount); //- sixten: parse items for(u64 ItemIndex = 0; ItemIndex < SceneView->NavItemCount; ItemIndex += 1) { scene_nav_item *Item = &SceneView->NavItems[ItemIndex]; for(;;) { switch(*Byte++) { case S_NavItemOp_TextureID: { string AssetName; AssetName.Count = *(u16 *)Byte; Byte += sizeof(u16); AssetName.Data = Byte; Byte += AssetName.Count; Item->TextureID = AssetID_Error; for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1) { if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName)) { Item->TextureID = AssetIndex; break; } } } goto Next; case S_NavItemOp_Scale: { Item->Scale = *(r32 *)Byte; Byte += sizeof(r32); } goto Next; case S_NavItemOp_Origin: { Item->Origin = *(v2_r32 *)Byte; Byte += sizeof(v2_r32); } goto Next; case S_NavItemOp_P: { Item->P = *(v2_r32 *)Byte; Byte += sizeof(v2_r32); } goto Next; case S_NavItemOp_HoverText: { Item->HoverText.Count = *(u16 *)Byte; Byte += 2; Item->HoverText.Data = PushArrayNoClear(SceneView->SceneArena, u8, Item->HoverText.Count); Copy(Item->HoverText.Data, Byte, Item->HoverText.Count); Byte += Item->HoverText.Count; } goto Next; case S_NavItemOp_Action: { Item->Action.Kind = (scene_nav_action_kind)*Byte; Byte += 1; Item->Action.Content.Count = *(u16 *)Byte; Byte += 2; Item->Action.Content.Data = PushArrayNoClear(SceneView->SceneArena, u8, Item->Action.Content.Count); Copy(Item->Action.Content.Data, Byte, Item->Action.Content.Count); Byte += Item->Action.Content.Count; } goto Next; } //- sixten: no op found, assume item done break; Next:; } } } ReleaseScratch(Scratch); } static void SV_SetCurrentSource(compiled_scene2 *Compiled) { scene_view *SceneView = SV_GetState(); scene2_runtime *Runtime = &SceneView->Runtime; // sixten(TODO): extract runtime information required to seamlessly transition between compilations SV_Reset(); ArenaClear(SceneView->SceneArena); SceneView->Compiled = S2_CopyCompiledScene(SceneView->SceneArena, Compiled); S2_SetCurrentProc(Runtime, S2_ProcFromName(Compiled, StrLit("main"))); } static void SV_Init(scene_view *SceneView) { SV_SetState(SceneView); SceneView->SceneArena = ArenaAlloc(Kilobytes(16), true, "Scene View Scene Arena"); SceneView->MessageArena = ArenaAlloc(Kilobytes(16), true, "Scene View Message Arena"); S2_Init(&SceneView->Runtime); SceneView->Runtime.Data = SceneView; SV_Reset(); } static b32 SV_CurrentlyInProc(void) { scene_view *SceneView = SV_GetState(); b32 Result = (SceneView->Runtime.CurrentProc.Name.Count != 0); return(Result); } static scene_view_character_data *SV_FindTalkingCharacter(void) { scene_view *SceneView = SV_GetState(); scene_view_character_data *Result = 0; for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Character = SceneView->Characters+CharacterIndex; if(Character->Talking) { Result = Character; break; } } return(Result); } struct text_properties { font_id Font; r32 FontSize; r32 LineHeight; }; static void RenderAnimatedText(render_group *Group, glyph_atlas *Atlas, text_properties Properties, string Text, r32 CharsToRender, v2_r32 P, r32 WrapWidth) { v2_r32 Offset = V2R32(0, 0); v2_r32 ShadowOffset = V2R32(Properties.FontSize*0.1, Properties.FontSize*0.1); u8 *TextBegin = Text.Data; u8 *TextEnd = TextBegin+Text.Count; u8 *Byte = TextBegin; u8 *WordBegin = TextBegin; u8 *WordEnd = WordBegin; u8 *TrueWordEnd = WordBegin; string_decode LastVisibleDecode = {}; Byte = TextBegin; for(;Byte<=TextEnd;) { string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); if(CharsToRender >= 1) { LastVisibleDecode = Decode; } if(Decode.Codepoint == ' ' || Byte==TextEnd) { string TrueWord = MakeString(WordBegin, TrueWordEnd); string Word = MakeString(WordBegin, WordEnd); r32 WordWidth = CalculateRasterizedTextWidth(Atlas, Properties.Font, Properties.FontSize, TrueWord); if(Offset.x+WordWidth > WrapWidth) { Offset.x = 0; Offset.y += Properties.LineHeight; } PushText(Group, Atlas, Properties.Font, P+Offset+ShadowOffset, Properties.FontSize, Color_Black, Word); Offset.x += PushText(Group, Atlas, Properties.Font, P+Offset, Properties.FontSize, Color_White, Word); if(WordEnd != TrueWordEnd) { string LastChar = MakeString(WordEnd, LastVisibleDecode.Size); r32 TransitionAmount = Mod(CharsToRender, 1); Offset.y += (1-TransitionAmount)*5; PushText(Group, Atlas, Properties.Font, P+Offset, Properties.FontSize, SetAlpha(Color_White, TransitionAmount), LastChar); } WordBegin = TrueWordEnd; if(Byte == TextEnd || CharsToRender < 1) { break; } } Byte += Decode.Size; TrueWordEnd += Decode.Size; if(CharsToRender >= 1) { WordEnd += Decode.Size; CharsToRender -= 1; } } } static r32 CalculateGlobalScaleFromDim(v2_r32 Dim) { r32 GlobalScale = SquareRoot(Dim.x*Dim.y)/45; return(GlobalScale); } static r32 CalculateGlobalScaleFromRootBox(ui_box *Box) { v2_r32 RenderDim = UI_CalculateBoxDim(Box); r32 GlobalScale = SquareRoot(RenderDim.x*RenderDim.y)/45; return(GlobalScale); } struct scene_textbox_data { char *TextboxData; u64 TextboxUsed; r32 TextboxRevealed; ui_box *SceneViewBox; string Name; r32 NameT; }; UI_CUSTOM_DRAW_CALLBACK(BuildSceneTextboxDrawCallback) { scene_textbox_data *TextboxData = (scene_textbox_data *)Data; r32 GlobalScale = CalculateGlobalScaleFromRootBox(TextboxData->SceneViewBox); //- sixten: render textbox { v4_r32 TopColor = V4R32(0, 0, 0, 0.8f); v4_r32 BottomColor = V4R32(0, 0, 0, 0.5f); range2_r32 Dest = Pad(Range2R32(Box->Rect.Min, Box->Rect.Max), V2R32(GlobalScale, GlobalScale)*2.0f); PushQuad(Group, Dest, TopColor, TopColor, BottomColor, BottomColor, 0, GlobalScale, 0); } //- sixten: render text { string Text = MakeString((u8 *)TextboxData->TextboxData, TextboxData->TextboxUsed); r32 RevealedT = TextboxData->TextboxRevealed; text_properties Properties = {}; Properties.Font = Font_Fancy; Properties.FontSize = GlobalScale; Properties.LineHeight = GlobalScale*1.5f; r32 Padding = 1.5f*GlobalScale; v2_r32 Offset = V2R32(Padding, Padding); RenderAnimatedText(Group, Atlas, Properties, Text, RevealedT, Box->Rect.Min+Offset, DimOfRange(Box->Rect).x-2*Padding); } //- sixten: render character name { string Name = TextboxData->Name; r32 T = TextboxData->NameT; v2_r32 TextP = Box->Rect.Min + V2R32(1.5f*GlobalScale, -GlobalScale*T); PushText(Group, Atlas, Font_Fancy, TextP+V2R32(GlobalScale*0.1, GlobalScale*0.1), GlobalScale, SetAlpha(Color_Black, T), Name); PushText(Group, Atlas, Font_Fancy, TextP, GlobalScale, SetAlpha(Color_White, T), Name); } } static void SV_DrawBackground(scene_view *SceneView, ui_box *Box, render_group *Group) { v2_r32 RenderDim = DimOfRange(Box->Rect); render_handle Texture = A_TextureFromAssetID(AssetID_DemoBackground);;//A_TextureFromAssetID(SceneView->Runtime.Compiled.BackgroundTexture); //- sixten: render background #if 0 persist r32 Time = 0; Time += 1 / 1200.0f; r32 r = 30; v2_r32 Offset = V2R32(Sin(Time)+0.5*Sin(43+2.43*Time)+Sin(424+Time*16)*0.1, Sin(8+Time)+0.5*Sin(43+2.43*Time)+Sin(4242+Time*16)*0.1)*(1.0f/1.6f)*r; range2_r32 BackgroundDest = Range2R32(Box->Rect.Min-V2R32(r, r)+Offset, RenderDim+Box->Rect.Min+V2R32(r, r)+Offset); range2_r32 BackgroundSource = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(Texture))); PushTexturedQuad(Group, BackgroundDest, BackgroundSource, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, Texture); #else range2_r32 BackgroundDest = Range2R32(Box->Rect.Min, RenderDim+Box->Rect.Min); range2_r32 BackgroundSource = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(Texture))); PushTexturedQuad(Group, BackgroundDest, BackgroundSource, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, 0, 0, 0, Texture); #endif } UI_CUSTOM_DRAW_CALLBACK(BuildSceneDrawCallback) { scene_view *SceneView = (scene_view *)Data; SV_DrawBackground(SceneView, Box, Group); v2_r32 RenderDim = DimOfRange(Box->Rect); r32 GlobalScale = CalculateGlobalScaleFromDim(RenderDim); //- sixten: render characters for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Character = SceneView->Characters + CharacterIndex; v4_r32 BlendColor = LinearBlend(Color_White, Color_Black, 0.5-Character->TalkingT*0.5); BlendColor.a = Character->ActiveT; r32 Scale = (Character->Info.Scale + Character->TalkingT*0.001)*(0.95+Character->ActiveT*0.05)*GlobalScale; render_handle CharacterHandle = Character->Info.Texture; v2_r32 CharacterDim = ConvertV2ToR32(DimFromTexture(CharacterHandle)); v2_r32 CharacterOriginP = V2R32(RenderDim.x*Character->PctP, RenderDim.y); v2_r32 CharacterMidP = Box->Rect.Min+V2R32(CharacterOriginP.x, CharacterOriginP.y - CharacterDim.y*Scale/2); range2_r32 CharacterDest = Range2R32(CharacterMidP-CharacterDim*0.5f*Scale, CharacterMidP+CharacterDim*0.5f*Scale); range2_r32 CharacterSource = Range2R32(V2R32(0, 0), CharacterDim); PushTexturedQuad(Group, CharacterDest, CharacterSource, BlendColor, BlendColor, BlendColor, BlendColor, 0, 0, 0, 0, 0, 0, CharacterHandle); } } static b32 BuildSceneBranchButton(string Text, r32 GlobalScale) { UI_SetNextFontSize(GlobalScale); UI_SetNextFont(Font_Fancy); UI_SetNextCornerRadius(5.0f); b32 Result = UI_Button(Text).Clicked; return(Result); } static void BuildProcView(scene_view *View, ui_box *Box, v2_r32 BoxDim) { temp Scratch = GetScratch(); r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim); r32 ActiveScale = GlobalScale * 0.75f; //scene2_runtime *Runtime = &View->Runtime; //textbox *Textbox = &View->Textbox; //- sixten: build branches UI_FillPadding UI_WidthFill UI_Height(UI_Pixels(2*ActiveScale, 1)) UI_Row() UI_FillPadding UI_Column() UI_FillPadding { #if 0 b32 FoundOffset = false; s64 Offset = 0; for(s32 BranchIndex = 0; BranchIndex < Runtime->BranchCount; BranchIndex += 1) { branch_case *Branch = &Runtime->Branches[BranchIndex]; if(BuildSceneBranchButton(PushFormat(Scratch.Arena, "%S#%i", Branch->Name, BranchIndex), ActiveScale)) { Offset = Branch->Offset; FoundOffset = true; } if(BranchIndex != Runtime->BranchCount - 1) { UI_Spacer(UI_Em(1, 1)); } } if(FoundOffset) { Runtime->IP += 1+Offset; Runtime->BranchCount = 0; } #endif } //- sixten: build textbox UI_Size(UI_Percent(1, 1), UI_Percent(0.3, 1)) { ui_box *TextBox = UI_MakeBox(0, StrLit("Scene Textbox")); scene_textbox_data *TextboxData = PushStruct(UI_FrameArena(), scene_textbox_data); TextboxData->TextboxData = View->TextboxData; TextboxData->TextboxUsed = View->TextboxUsed; TextboxData->TextboxRevealed = View->TextboxRevealed; TextboxData->SceneViewBox = Box; scene_view_character_data *TalkingCharacter = SV_FindTalkingCharacter(); TextboxData->Name = TalkingCharacter ? TalkingCharacter->Name : StrLit(""); TextboxData->NameT = AC_AnimateValueF(TalkingCharacter != 0, 0, 0.3f, "Scene View Talking Character %p", View); UI_EquipBoxCustomDrawCallback(TextBox, BuildSceneTextboxDrawCallback, TextboxData); } ReleaseScratch(Scratch); } struct scene_nav_item_info { scene_nav_item *Item; ui_signal Signal; }; UI_CUSTOM_DRAW_CALLBACK(BuildNavItemDrawCallback) { scene_nav_item_info *Info = (scene_nav_item_info *)Data; render_handle Texture = A_TextureFromAssetID(Info->Item->TextureID); v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); range2_r32 DestRect = Range2R32(Box->Rect.Min, Box->Rect.Max); range2_r32 SourceRect = Range2R32(V2R32(0, 0), TextureDim); v4_r32 Color = LinearBlend(Color_Grey, Color_White, Info->Signal.Hovering); PushTexturedQuad(Group, DestRect, SourceRect, Color, Color, Color, Color, 0, 0, 0, 0, 0, 0, Texture); } inline u32 U32FromRawR32(r32 Value) { u32 Result = *(u32 *)&Value; return(Result); } static ui_signal SV_BuildNavItem(scene_nav_item *Item, r32 GlobalScale, v2_r32 GlobalDim) { r32 AppliedScale = GlobalScale*Item->Scale; scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info); Data->Item = Item; render_handle Texture = A_TextureFromAssetID(Item->TextureID); v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin); v2_r32 OffsetP = GlobalDim*(V2R32(1, 1) + Item->P)*0.5f; v2_r32 OriginP = TextureOrigin*AppliedScale; v2_r32 Dim = TextureDim*AppliedScale; range2_r32 Dest = Range2R32(OffsetP-OriginP, OffsetP-OriginP+Dim); UI_SetNextFixedP(Dest.Min); UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1)); UI_SetNextHoverCursor(PlatformCursor_Hand); ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, "View Item Box %i %S %p", (U32FromRawR32(Item->Scale)<<4)+ (U32FromRawR32(Item->Origin.x)<<17)+ (U32FromRawR32(Item->Origin.y)<<11), Item->HoverText, Item); UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data); ui_signal Signal = UI_SignalFromBox(ItemBox); Data->Signal = Signal; return(Signal); } static ui_signal BuildNavItemAndLabel(scene_nav_item *Item, r32 GlobalScale, v2_r32 GlobalDim) { ui_signal Signal = SV_BuildNavItem(Item, GlobalScale, GlobalDim); if(Item->HoverText.Count && Signal.Hovering) { UI_FontSize(GlobalScale*0.5f) UI_Font(Font_Fancy) UI_CornerRadius(4.0f) UI_TooltipLabel(Item->HoverText, UI_MouseP()); } return(Signal); } static UI_CUSTOM_DRAW_CALLBACK(Puzzle15DrawCallback) { scene_view *SceneView = (scene_view *)Data; render_handle Texture = A_TextureFromAssetID(AssetID_Duck); v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); v2_r32 PieceDim = DimOfRange(Box->Rect)*0.25f; v2_r32 Offset = Box->Rect.Min+V2R32(0, (1.0f - SceneView->BoardOpenT)*PieceDim.y); if(SceneView->TimeSinceBoardClear < 2) { for(s32 BoardY = 0; BoardY < 4; BoardY += 1) { for(s32 BoardX = 0; BoardX < 4; BoardX += 1) { v2_r32 SourceDim = TextureDim*0.25f; u8 BoardValue = SceneView->Board[BoardY][BoardX]; if(BoardValue) { s32 PrevBoardX = BoardX; s32 PrevBoardY = BoardY; if(SceneView->PrevBoard[PrevBoardY][PrevBoardX] != BoardValue) { for(PrevBoardY = 0; PrevBoardY < 4; PrevBoardY += 1) { for(PrevBoardX = 0; PrevBoardX < 4; PrevBoardX += 1) { if(SceneView->PrevBoard[PrevBoardY][PrevBoardX] == BoardValue) { goto FoundPrevTile; } } } FoundPrevTile:; } BoardValue -= 1; s32 SourceX = BoardValue % 4; s32 SourceY = BoardValue / 4; v2_r32 BoardP = LinearBlend(V2R32(PrevBoardX, PrevBoardY), V2R32(BoardX, BoardY), Clamp01(Pow(SceneView->TimeSinceBoardUpdate*5, 4))); range2_r32 Source = Range2R32(V2R32(SourceX*SourceDim.x, SourceY*SourceDim.y), V2R32((SourceX+1)*SourceDim.x, (SourceY+1)*SourceDim.y)); range2_r32 Dest = Range2R32(Offset+BoardP*PieceDim, Offset+(BoardP+V2R32(1, 1))*PieceDim); v4_r32 Color = SetAlpha(Color_White, SceneView->BoardOpenT); PushTexturedQuad(Group, Dest, Source, Color, Color, Color, Color, 3, 3, 3, 3, 1.5f, 0, Texture); } } } } if(SceneView->BoardIsComplete) { r32 CompleteT = Clamp01(Pow(SceneView->TimeSinceBoardClear, 5))*Clamp01(3-SceneView->TimeSinceBoardClear); range2_r32 Source = Range2R32(V2R32(0, 0), TextureDim); range2_r32 Dest = Pad(Box->Rect, PieceDim*(1.0f-CompleteT)); v4_r32 Color = SetAlpha(Color_White, CompleteT); PushTexturedQuad(Group, Dest, Source, Color, Color, Color, Color, 3, 3, 3, 3, 1.5f, 0, Texture); } } static void BuildScene(scene_view *SceneView) { v2_r32 ParentDim = UI_CalculateBoxDim(UI_TopParent()); r32 TargetRatio = 16.0f/9.0f; r32 ActualRatio = ParentDim.x/ParentDim.y; v2_r32 BoxDim = ParentDim; if(ActualRatio > TargetRatio) { BoxDim.x = BoxDim.y*TargetRatio; } else { BoxDim.y = BoxDim.x/TargetRatio; } UI_SetNextWidth(UI_Pixels(BoxDim.x, 1)); UI_SetNextHeight(UI_Pixels(BoxDim.y, 1)); UI_SetNextLayoutAxis(Axis2_Y); UI_SetNextFixedP((ParentDim-BoxDim)*0.5f); ui_box *Box = UI_MakeBox(UI_BoxFlag_Clip|UI_BoxFlag_DrawDropShadow|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, StrLit("Scene View")); UI_EquipBoxCustomDrawCallback(Box, BuildSceneDrawCallback, SceneView); r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim); UI_Parent(Box) { switch(SceneView->Mode) { case SV_Mode_Scene: { // sixten(NOTE): when this is rewritten, put it inside THIS function. BuildProcView(SceneView, Box, BoxDim); } break; case SV_Mode_Nav: { for(int ItemIndex = 0; ItemIndex < SceneView->NavItemCount; ++ItemIndex) { scene_nav_item *Item = SceneView->NavItems+ItemIndex; if(BuildNavItemAndLabel(Item, GlobalScale, BoxDim).Clicked) { // sixten: apply the action if(Item->Action.Kind == S_NavAction_Proc) { S2_SetCurrentProc(&SceneView->Runtime, S2_ProcFromName(&SceneView->Compiled, Item->Action.Content)); } else if(Item->Action.Kind == S_NavAction_Scene) { temp Scratch = GetScratch(); string Filepath = PushFormat(Scratch.Arena, "data/%S.vns", Item->Action.Content);; string SceneInput = Platform_ReadEntireFile(Scratch.Arena, Filepath); compiled_scene2 Scene = S2_CompiledFromString(Scratch.Arena, SceneInput); SV_SetCurrentSource(&Scene); ReleaseScratch(Scratch); } } } } break; case SV_Mode_P15: { UI_WidthFill UI_HeightFill UI_BackgroundColor(SetAlpha(Color_Black, 0.5*SceneView->BoardOpenT*Clamp01(2-SceneView->TimeSinceBoardClear*0.5))) UI_LayoutAxis(Axis2_X) UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit("15 Puzzle Darken"))) { UI_Padding(UI_Percent(1, 0)) UI_Width(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Percent(1, 0)) { UI_SetNextSize(UI_Em(25, 1), UI_Em(25, 1)); ui_box *PuzzleBox = UI_MakeBox(0, StrLit("Puzzle Box")); UI_EquipBoxCustomDrawCallback(PuzzleBox, Puzzle15DrawCallback, SceneView); } } } break; } } } static void BuildErrorScreen(scene_view *SceneView, vn_input *Input) { UI_SetNextLayoutAxis(Axis2_X); UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit("Container"))) { UI_Padding(UI_Em(3, 1)) UI_Width(UI_Percent(1, 0)) UI_Column() UI_Padding(UI_Em(3, 1)) { UI_Font(Font_Bold) UI_Size(UI_TextContent(0, 1), UI_TextContent(0, 1)) UI_FontSize(32) UI_LabelF("A runtime error has occurred"); s64 ErrorIndex = 0; for(scene2_message *Message = SceneView->Messages.First; Message != 0; Message = Message->Next, ErrorIndex += 1) { UI_Spacer(UI_Em(3, 1)); UI_SetNextCornerRadius(3); UI_Size(UI_Percent(1, 1), UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawDropShadow|UI_BoxFlag_DrawBorder, "%i", ErrorIndex)) UI_Size(UI_TextContent(30, 1), UI_TextContent(30, 1)) { UI_Label(Message->Message); } } UI_Spacer(UI_Em(3, 1)); UI_Size(UI_Percent(1, 1), UI_Em(2, 1)) UI_Row() UI_Width(UI_TextContent(30, 1)) UI_CornerRadius(4) { ui_signal IgnoreSignal = UI_ButtonF("Ignore"); if(IgnoreSignal.Hovering) { UI_TooltipLabel(StrLit("Continue running the script, may lead to more errors."), UI_MouseP()); } if(IgnoreSignal.Clicked) { ZeroStruct(&SceneView->Messages); ArenaClear(SceneView->MessageArena); } UI_Spacer(UI_Em(1, 1)); ui_signal RestartSignal = UI_ButtonF("Restart"); if(RestartSignal.Hovering) { UI_TooltipLabel(StrLit("Restarts the script, may lose progress."), UI_MouseP()); } if(RestartSignal.Clicked) { SV_Reset(); } UI_Spacer(UI_Em(1, 1)); if(UI_ButtonF("Exit Program").Clicked) { Input->ExitRequested = true; } } } } } static scene_view_character_data *SV_CharacterDataFromName(string Name) { scene_view_character_data *Result = 0; scene_view *View = SV_GetState(); for(s32 CharacterIndex = 0; CharacterIndex < View->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Character = View->Characters + CharacterIndex; if(AreEqual(Character->Name, Name)) { Result = Character; break; } } //- sixten: create character if not initialized if(!Result && View->CharactersUsed < ArrayCount(View->Characters)) { s32 CharacterIndex = View->CharactersUsed; View->CharactersUsed += 1; Result = View->Characters + CharacterIndex; *Result = {}; Result->Name = Name; Result->Active = true; Result->PctP = (r32)(CharacterIndex + 1) / (View->CharactersUsed + 1); } return(Result); } static r32 SV_CalculateTargetPctP(s32 TrueCharacterIndex) { scene_view *View = SV_GetState(); s32 CharacterCount = 0; s32 AssumedCharacterIndex = 0; for(s32 CharacterIndex = 0; CharacterIndex < View->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Data = View->Characters + CharacterIndex; if(Data->Active) { CharacterCount += 1; if(CharacterIndex < TrueCharacterIndex) { AssumedCharacterIndex += 1; } } } r32 Result = (r32)(AssumedCharacterIndex + 1) / (Max(CharacterCount, 1) + 1); return(Result); } static void SV_UpdateInDialog(arena *FrameArena) { scene_view *SceneView = SV_GetState(); //- sixten: gather user input b32 MousePressed = false; for(platform_event *Event = SceneView->EventList->First; Event != 0; Event = Event->Next) { if(Event->Type == PlatformEvent_Press && Event->Key == Key_MouseLeft) { MousePressed = true; } } b32 PlayerAction = (Platform_KeyPress(SceneView->EventList, Key_Space)||MousePressed); //- sixten: check if we can skip the current textbox if(PlayerAction && SceneView->TextboxRevealed < SceneView->TextboxUsed) { PlayerAction = false; SceneView->TextboxRevealed = SceneView->TextboxUsed; } //- sixten: update the textbox r32 CharsPerSecond = 25.0f; SceneView->TextboxRevealed = Min((r32)SceneView->TextboxUsed, SceneView->TextboxRevealed+CharsPerSecond*SceneView->dtForFrame); //- sixten: run the runtime scene2_run_result RunResult = S2_Run(FrameArena, &SceneView->Runtime, &SceneView->Compiled, PlayerAction); //- sixten: append messages S2_ConcatMessageList(SceneView->MessageArena, &SceneView->Messages, &RunResult.Messages); for(scene2_action *Action = RunResult.Actions.First; Action != 0; Action = Action->Next) { switch(Action->Kind) { case S2_ActionKind_None: { InvalidCodepath; } break; InvalidDefaultCase; } } #if 0 scene_view *SceneView = SV_GetState(); textbox *Textbox = &SceneView->Textbox; scene2_runtime *Runtime = &SceneView->Runtime; platform_event_list *EventList = SceneView->EventList; r32 dtForFrame = SceneView->dtForFrame; compiled_scene2 *Compiled = &Runtime->Compiled; if(Compiled && Compiled->IsValid) { //- sixten: gather user input b32 MousePressed = false; for(platform_event *Event = EventList->First; Event != 0; Event = Event->Next) { if(Event->Type == PlatformEvent_Press && Event->Key == Key_MouseLeft) { MousePressed = true; } } b32 PlayerAction = (Platform_KeyPress(EventList, Key_Space)||MousePressed); //- sixten: run the scene if(!Runtime->LastResult.HadError) { b32 AdvanceOnAwait = (Textbox->CharsRevealed >= Textbox->String.Count) && PlayerAction; for(;;) { scene_runtime_result RunResult = S_Run(Runtime, FrameArena, AdvanceOnAwait); if(RunResult.ReachedAwait || RunResult.HadError) { break; } } } //- sixten: advance textbox { r32 CharsPerSecond = 25.0f; Textbox->CharsRevealed += dtForFrame*CharsPerSecond; Textbox->CharsRevealed = Min(Textbox->CharsRevealed, (r32)Textbox->String.Count); if(Textbox->CharsRevealed < Textbox->String.Count && PlayerAction) { Textbox->CharsRevealed = Textbox->String.Count; } AC_AnimateValueDirect(Textbox->CharsRevealed, 0.05f, &Textbox->CharsRevealedT); } //- sixten: apply the textbox actions for(scene_textbox_action *Action = Runtime->FirstTextboxAction; Action != 0; Action = Action->Next) { if(Action->Kind == S_TextboxActionKind_Set) { string ReplaceString = Action->String; Textbox->String.Count = Min(ReplaceString.Count, Textbox->Capacity); Copy(Textbox->String.Data, ReplaceString.Data, Textbox->String.Count); Textbox->CharsRevealedT = 0; Textbox->CharsRevealed = 0; } else if(Action->Kind == S_TextboxActionKind_Append) { string Addend = Action->String; Textbox->CharsRevealedT = Textbox->String.Count; s64 NewCount = Min(Textbox->String.Count+Addend.Count, Textbox->Capacity-1); Copy(Textbox->String.Data+Textbox->String.Count, Action->String.Data, NewCount-Textbox->String.Count); Textbox->String.Count = NewCount; } else { InvalidCodepath; } } Runtime->FirstTextboxAction = Runtime->LastTextboxAction = 0; // sixten: update character state only if there has been a change if(Runtime->FirstCharacterAction != 0) { //- sixten: make all characters non-talking for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Data = SceneView->Characters + CharacterIndex; Data->Talking = false; } //- sixten: apply character actions for(scene_character_action *Action = Runtime->FirstCharacterAction; Action != 0; Action = Action->Next) { // sixten: find character scene_view_character_data *Data = SV_CharacterDataFromName(Action->Target); if(Action->StateModified) { if(Action->State == AssetID_None) { Data->Active = false; } else { Data->Info = SV_CharacterTextureFromAction(Action); Data->Talking = true; } } else { Data->Talking = true; } } } Runtime->FirstCharacterAction = Runtime->LastCharacterAction = 0; } #endif } static void SV_Update(arena *FrameArena) { scene_view *SceneView = SV_GetState(); //- sixten: update the characters { for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Data = SceneView->Characters + CharacterIndex; if(!SV_CurrentlyInProc()) { Data->Active = false; Data->Talking = false; } AC_AnimateValueDirect(Data->Active, 0.5f, &Data->ActiveT); AC_AnimateValueDirect(Data->Talking, 0.4f, &Data->TalkingT); r32 TargetPctP; if(Data->Active) { TargetPctP = SV_CalculateTargetPctP(CharacterIndex); } else { TargetPctP = (r32)(CharacterIndex+1)/(SceneView->CharactersUsed+1); } AC_AnimateValueDirect(TargetPctP, 0.4f, &Data->PctP); } // sixten: prune any unactive characters for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharactersUsed; CharacterIndex += 1) { scene_view_character_data *Data = SceneView->Characters + CharacterIndex; if(!Data->Active && Data->ActiveT < 0.01) { Move(Data, Data+1, (SceneView->CharactersUsed-CharacterIndex-1)*sizeof(scene_view_character_data)); SceneView->CharactersUsed -= 1; CharacterIndex -= 1; } } } // sixten(HACK): if a scene has been triggered, switch mode if(SceneView->Mode == SV_Mode_Nav && SV_CurrentlyInProc()) { SceneView->Mode = SV_Mode_Scene; } switch(SceneView->Mode) { case SV_Mode_Scene: { //- sixten: gather user input b32 MousePressed = false; for(platform_event *Event = SceneView->EventList->First; Event != 0; Event = Event->Next) { if(Event->Type == PlatformEvent_Press && Event->Key == Key_MouseLeft) { MousePressed = true; } } b32 PlayerAction = (Platform_KeyPress(SceneView->EventList, Key_Space)||MousePressed); //- sixten: check if we can skip the current textbox if(PlayerAction && SceneView->TextboxRevealed < SceneView->TextboxUsed) { PlayerAction = false; SceneView->TextboxRevealed = SceneView->TextboxUsed; } //- sixten: update the textbox r32 CharsPerSecond = 25.0f; SceneView->TextboxRevealed = Min((r32)SceneView->TextboxUsed, SceneView->TextboxRevealed+CharsPerSecond*SceneView->dtForFrame); //- sixten: run the runtime scene2_run_result RunResult = S2_Run(FrameArena, &SceneView->Runtime, &SceneView->Compiled, PlayerAction); //- sixten: append messages S2_ConcatMessageList(SceneView->MessageArena, &SceneView->Messages, &RunResult.Messages); for(scene2_action *Action = RunResult.Actions.First; Action != 0; Action = Action->Next) { switch(Action->Kind) { case S2_ActionKind_None: { InvalidCodepath; } break; InvalidDefaultCase; } } // sixten: check if the scene is finished if(!SV_CurrentlyInProc() && SceneView->Mode == SV_Mode_Scene) { SceneView->Mode = SV_Mode_Nav; } } break; case SV_Mode_Nav: { } break; case SV_Mode_P15: { SceneView->TimeSinceBoardUpdate += SceneView->dtForFrame; AC_AnimateValueDirect(1, 2.0f, &SceneView->BoardOpenT); if(SceneView->BoardIsComplete) { SceneView->TimeSinceBoardClear += SceneView->dtForFrame; if(SceneView->TimeSinceBoardClear > 3.5f) { SceneView->Mode = SV_Mode_Scene; } } else { v2_s32 BoardDelta = V2S32(0, 0); if(Platform_KeyPress(SceneView->EventList, Key_Up)) { BoardDelta.y += 1; } if(Platform_KeyPress(SceneView->EventList, Key_Down)) { BoardDelta.y -= 1; } if(Platform_KeyPress(SceneView->EventList, Key_Left)) { BoardDelta.x += 1; } if(Platform_KeyPress(SceneView->EventList, Key_Right)) { BoardDelta.x -= 1; } if(BoardDelta.x != 0 || BoardDelta.y != 0) { if(BoardDelta.x && BoardDelta.y) { BoardDelta.y = 0; } // sixten: find the current empty square v2_s32 EmptyP = V2S32(-10, -10); for(s32 BoardY = 0; BoardY < 4; BoardY += 1) { for(s32 BoardX = 0; BoardX < 4; BoardX += 1) { if(SceneView->Board[BoardY][BoardX] == 0) { EmptyP = V2S32(BoardX, BoardY); goto FoundEmptyP; } } } FoundEmptyP:; // sixten: check if we are able to perform an action range2_s32 BoardRange = Range2S32(V2S32(0, 0), V2S32(4, 4)); v2_s32 NewEmptyP = EmptyP+BoardDelta; if(InRange(BoardRange, NewEmptyP)) { SceneView->TimeSinceBoardUpdate = 0; Copy(SceneView->PrevBoard, SceneView->Board, sizeof(SceneView->Board)); SceneView->Board[EmptyP.y][EmptyP.x] = SceneView->Board[NewEmptyP.y][NewEmptyP.x]; SceneView->Board[NewEmptyP.y][NewEmptyP.x] = 0; } // sixten: check if board is completed b32 BoardIsComplete = true; for(s32 BoardY = 0; BoardY < 4; BoardY += 1) { for(s32 BoardX = 0; BoardX < 4; BoardX += 1) { if(SceneView->Board[BoardY][BoardX] != (1+BoardX+BoardY*4)%16) { BoardIsComplete = false; goto BoardNotComplete; } } } BoardNotComplete:; if(BoardIsComplete) { SceneView->BoardIsComplete = true; } } } } break; } } static void SV_BuildSceneView(vn_input *Input) { scene_view *SceneView = SV_GetState(); if(SceneView->Messages.Count != 0) { BuildErrorScreen(SceneView, Input); } else { BuildScene(SceneView); } }