Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Crimson Wizard

#1081
Quote from: Snarky on Mon 02/09/2024 16:37:31In LucasArts speech mode it is also possible to override the speech animation by setting each frame manually.

Does not it let to do this in any speech style?
Oh right, in Sierra style you have to change not Character.Frame, but Speech.PortraitOverlay.Graphic.

But yes, there are two alternatives in general:
- Use Animate command to run a loop, or chain loop after loop in a sequence;
- Change current view frame by frame. That is more low-level but has the most precise control.
#1082
Quote from: FortressCaulfield on Mon 02/09/2024 15:49:39I was hoping to do it by specifying the start and end frame of the talk loop without having to have different loops. So for a long animation, the character will start by shifting her weight to her other leg, then gesticulating, and so on, but for scenes where she has several say commands in a row, I don't want her starting the loop all over again. I was hoping I'd be able to do something like cOva.SayX("I never liked turtles!", 5, 17); and have that particular say animation only use frames 5 to 17 and repeat.

Default speech animation does not let specify exact frames (unless that's a lipsync, but that is a completely separate story).

For a custom animation, Animate command supports a starting frame argument, but not the ending frame or frame range. So unless that's implemented in the engine, Animate alone cannot be used for this either.

Which other possibilities exist?

First of all, you could instead arrange frames for different moods and gestures in separate loops. That's how people usually do that: a single animation per single loop. I haven't heard if anyone used multiple animations per loop.

If you want different animations to play while the same text line is on screen, then you won't be able to use default Say command here anyway. Instead you'd need to write a custom function that combines several things together:
- Creates a textual overlay with the text and displays it on screen;
- Plays a sequence of animations.
- Lets to interrupt this sequence in anytime by player's input.
- Stops animation, removes textual overlay.

AGS provides necessary commands for this, the problem here is to plan such function in script and code it.

Other things that may be considered: constructing a character out of multiple characters that animate separately: body, gesticulating hands, speaking face. That's also possible with a custom function.
#1083
You cannot modify existing engine functions without modifying the engine, but you can write your own functions in script that accept any arguments and follow any logic, using provided engine functions internally.

In a primitive case this may be something like:
Code: ags
enum CharacterMood
{
    eMoodNormal = 0,
    eMoodAngry = 1,
    eMoodPleased = 2,
    eMoodSad = 3
};

function SayMood(this Character*, const string text, CharacterMood mood)
{
    int old_speech_view = this.SpeechView;
    int use_view = this.SpeechView + mood; // calculate view as an offset to character's speech view
    this.SpeechView = use_view;
    this.Say(text);
    this.SpeechView = old_speech_view;
}

Code: ags
player.SayMood("I'm so angry!", eMoodAngry);
cGuy.SayMood("Well, I'm sad.", eMoodSad);
#1084
Quote from: lafouine88 on Sun 01/09/2024 19:50:28EDIT : One small thing though, the loading time (during the transition and new setting of the overlay,and drawing surfaces) is a bit longer since I added the new code for drawing surface. I downloaded the executable file in the "install" folder, but maybe I missed something ? Or is it just that walk behind are longer to settle?

Every operation takes time, painting a very big image takes noteable amount of time, plus engine has to parse pixels and "cut out" walk-behind sprites.

If the operation is the same, but takes longer than in the previous version unexpectedly, then that's something to investigate. I could not tell whether this is the case from what you've said, if it is, then please clarify that?

Speaking of walk-behinds in particular, if you want to speed things up then you may just create game objects or room overlays with background portions on them instead. Then you won't need to use the walk-behind mask. They will still take time to load up, but engine will not have to do copy-pasting on a surface, and then cutting things out again.
#1085
This is a ancient request, but I believe that converting all sprites to a new color depth can be done easily if you do "Recreate sprite file" or "Reimport sprites from sources" command after changing game's color depth.

Because editor always converts sprites to the game's depth on import.

I shall close this thread now.
#1086
Editor Development / Re: 32-bits templates
Sun 01/09/2024 18:32:29
Completed long time ago, so closed.
#1087
True 32-bit drawing colors are now supported in ags4 branch, will be a part of the next AGS 4 alpha update.

Respected Alpha component is still in TODO, but it may require more work, since all the primitive drawing operations must be affected, so I don't have any prediction as when this may be done.
https://github.com/adventuregamestudio/ags/issues/2525
#1088
If you want certain subtasks to not be visible, do not call AddTask with "addSubtasks" true.
Instead call it with "false" and then do AddSubTask for each subtask that you want to make active.

QuoteI'd prefer that the later on added subtask would go to the bottom of the substack list. Right now it seems to go in the middle, or one row below the first item? Is there a way to do it by editing the insertAt int?

When I wrote the code the intent was that the new active subtasks is appended after all other active subtasks, or right after parent if no other subtasks are active.

If it does not work like that, then there's a mistake in code.

EDIT: probably the condition is wrong, should be:
Code: ags
  for (int j = i + 1; (j < TaskReceivedCount) && (TaskReceived[j] > parentIndex && TaskReceived[j] < parentIndex + maxSubs); j++)
#1089
It appears that the old compiler cannot handle ++ operator inside brackets.

Code: ags
TaskReceived[TaskReceivedCount] = taskIndex;
TaskReceivedCount++;
^ this is a correct equivalent.

A full function will look like
Code: ags
function AddTask(int taskIndex, bool addSubtasks)
{
    TaskReceived[TaskReceivedCount] = taskIndex;
    TaskReceivedCount++;
    if (addSubtasks)
    {
        // add subtasks too (they have sequential indexes)
        for (int i = 0; i < GameTasks[taskIndex].NumSubTasks; i++)
        {
            TaskReceived[TaskReceivedCount] = taskIndex + 1 + i;
            TaskReceivedCount++;
        }
    }
}

EDIT: Ah, it should be "taskIndex + 1 + i;" for subtasks, fixed that mistake.
#1090
On a separate note, I think that above structure may also be used if there are more nested subtask levels.
The Task struct could use a "ParentID" field for easier finding a parent in case we need to go from child to parent when reading tasks.
And some of the operations from above will have to become recursive (calling same function from inside a function for the nested child level of tasks).
#1091
Here's an updated version, with walk-behinds problem fixed:
https://cirrus-ci.com/task/6249807860203520

(this is a full editor download from our build server)
#1092
You could add a separate array containing indexes of tasks in the order of them being received.
And then when you are updating GUI you iterate this array instead.

For example:
Code: ags
int TaskReceived[MAX_TASKS];
int TaskReceivedCount;

function AddTask(int taskIndex, bool addSubtasks)
{
    TaskReceived[TaskReceivedCount++] = taskIndex;
    if (addSubtasks)
    {
        // add subtasks too (they have sequential indexes)
        for (int i = 0; i < GameTasks[taskIndex].NumSubTasks; i++)
        {
            TaskReceived[TaskReceivedCount++] = taskIndex + i;
        }
    }
}

If a subtask has to be inserted later, then insert one in the middle, and shift all indexes

Code: ags
function AddSubTask(int parentIndex, int subIndex)
{
    int insertAt = -1;
    for (int i = 0; i < TaskReceivedCount; i++)
    {
        if (TaskReceived[i] == parentIndex)
        {
            insertAt = i + 1;
            // Find if other subtasks are already added and skip these
            int maxSubs = GameTasks[parentIndex].NumSubTasks;
            for (int j = i + 1; (j < TaskReceivedCount) && (TaskReceived[j] < parentIndex + maxSubs); j++)
            {
                insertAt++;
            }
        }
    }

    // Copy everything to the right to free the index, in the reverse order
    for (int i = TaskReceivedCount; i > insertAt; i--)
    {
        TaskReceived[i] = TaskReceived[i - 1];
    }

    // And insert a subtask on its place
    TaskReceived[insertAt] = subIndex;
    TaskReceivedCount++;
}




and then gui update:
Code: ags
function UpdateTasksGUI()
{
  lstTaskList.Clear();
  for (int i = 0; i < TaskReceivedCount; i++)
  {
    int taskID = TaskReceived[i];
    lstTaskList.AddItem(GameTasks[taskID].Name);
  }

And UpdateButtons changed accordingly.

Code: ags
function UpdateButtons()
{
<...>

  for (int i = 0; i < TaskReceivedCount; i++)
  {
    int taskID = TaskReceived[i];
    if (active_task_index >= top_index)
    {
         // Find the next available button on GUI
         int but_id = btnTaskList_0.ID + button_index;
         Button *button = taskListGUI.Controls[but_id].AsButton;
         button.Visible = GameTasks[taskID].Completed;
         button_index++; // button used, moved to next
       }
       active_task_index++
    }

<...>
}
#1093
So, I found that after painting walkbehinds engine does not update the walkbehind "cuts", so they would never cover the character.
This is another thing that was not tested properly.

All this feature is filled with mistakes, I must retest it fully, for all region types, and patch in 3.6.1
#1094
I was under impression that this feature is very wanted by a number of people, but as I keep posting here for 3 months, no one replies.

I think this needs to be tested by users, and not only for functionality, but also for usability, because I do not have a full confidence in the chosen design.

How do I proceed from here?
#1095
Quote from: rongel on Fri 30/08/2024 18:15:35Also not 100% sure what the "NumSubTasks" does actually? What would be the difference if I wouldn't use it?

This was more a prototype. But that may come useful if you need to distinguish subtasks. For example, you may want to indent a subtask with couple of spaces in a listbox. Then you'll need to find out which items are subtasks.
#1096
The traditional approach to this kind of task is in separating the Data and a View, where Data is stored in variables and View is a representation that shows the data on screen.

In practical sense this means that you should not make buttons rely on actual items inside ListBox, and instead make both controls rely on what is in variables, and which range of data are you displaying on screen right now.

Whenever you display a GUI on screen, you read tasks from the variables, and appoint ListBox contents and Buttons accordingly.

Whenever anything changes, while GUI is on screen (tasks added, removed, task state changes, etc), you run the update on it, and again read data from variables and reappoint ListBox and Buttons.

Because ListBox may have a lot of items in it but display only certain range at once, you'd also need to know that range and adjust buttons whenever ListBox is scrolled by the player. I don't know if you deal with this now, and if yes then how. In the simple case you just need to know the range: first index to display, and displayed count.

This approach, while seemingly more complex, will give you much more freedom in what and how do you display on screen.


How may be a list of tasks stored in variables? This may be an array of "Task" structs that describe them.
To give an example (I am making things up here, not knowing what do you have in your tasks).

Code: ags
struct Task
{
   String Name;
   bool Active;
   bool Complete;
   int NumSubTasks; // number of subtasks in this task
};

Task GameTasks[MAX_TASKS];

In this example, the subtasks are stored in the same "Task" array, following their parent tasks, and NumSubTasks is set in parent Task to tell how many subtasks does it have.
This is kind of a ugly structure, but works for a simpler case. If you need a bigger, expandable tree-like structure of tasks and subtasks, another structure would be required, but that's a broader topic.

On a game start initialize this array. Subtasks should follow the parent tasks, for example:
Code: ags
function game_start()
{
  GameTasks[0].Name = "Go shopping";
  GameTasks[0].NumSubTasks = 3; // next 3 tasks are subtasks of this one
  GameTasks[1].Name = "Buy food";
  GameTasks[2].Name = "Buy a dress";
  GameTasks[3].Name = "Buy a new carpet";
}

Whenever you add or remove your tasks, you do so inside this array of structs, e.g. by setting their Active bool to true or false.


Then the ListBox may be filled by running over GameTasks array, and fill in only items that you like to display: All, only Active, only Completed, etc, based on variables in struct.
The Buttons then are updated based on:
- the top index of displayed task
- the task's state


For a very crude example:
Code: ags
function UpdateTasksGUI()
{
  lstTaskList.Clear();
  for (int i = 0; i < MAX_TASKS; i++)
  {
    if (GameTasks[i].Active)
    {
       lstTaskList.AddItem(GameTasks[i].Name);
    }
  }

  UpdateButtons();
}

function OnTaskListScrolled()
{
  UpdateButtons();
}

function UpdateButtons()
{
  int top_index = ListBox.TopItem; // an index of active tasks visible on GUI
  int row_count = ListBox.RowCount; // how many active tasks are visible on GUI
  int active_task_index = 0; // an index of active tasks in a list
  int button_index = 0; // the next index of a button to use

  for (int i = 0; i < MAX_TASKS; i++)
  {
    if (GameTasks[i].Active)
    {
       if (active_task_index >= top_index)
       {
         // Find the next available button on GUI
         int but_id = btnTaskList_0.ID + button_index;
         Button *button = taskListGUI.Controls[but_id].AsButton;
         button.Visible = GameTasks[i].Completed;
         button_index++; // button used, moved to next
       }
       active_task_index++
    }

    if (active_task_index >= top_index + row_count)
    {
       break; // no more items are visible on gui
    }
  }
}
#1097
Quote from: RootBound on Wed 28/08/2024 12:35:12Will this allow the objects to come back when they reappear on screen?

Of course, since objects appear back inside the camera bounds, they immediately become marked as visible.
I was testing this in your game, and software renderer no longer crashes with out of memory error.
#1098
Quote from: eri0o on Wed 28/08/2024 11:57:12If we could figure an API at least the affine tramsform could maybe be added in the engine? (at least the skew from AGS waves could be added)

Alan have preplanned "skewx, skewy" properties a while ago; these may be found in AGS 4 save formats as placeholders.
I don't know what is the correct way to do this with texture renderers, that may be either changing texture vertices (moving corner coords to the sides), or using a shader, i suppose it's a matter of finding out.
#1099
I tried a bit different fix based on the current downloadable module:

Code: ags
this.Objects[obin].ScreenVisible = this.Objects[obin].Visible
      && (obj_d_x + w > this._screen_x) && (obj_d_y + h > this._screen_y)
      && (obj_d_x < this._screen_width) && (obj_d_y < this._screen_height);

That marks them not visible when they are off the virtual screen.

This is enough for objects that go into sides as camera moves forward, but still not enough to cull the objects which you approach directly, which would keep it in the camera's center. (I do not have a strong proof, because using a specific game scene for a test, but may imagine this case).

For a proper solution this should also include a z test, but I don't know the code well enough to figure this out quickly.


BTW, in regards to speed, the game I'm testing displays a camera moving along the railroad in a "straight line", with only few objects around,
and it runs pretty well in Software renderer, making ~50 fps at average on my machine.
I suppose that it may be feasible to aim at stable 30 fps mode7 scenes with this module.
#1100
Quote from: eri0o on Wed 28/08/2024 10:48:54In a new version I have removed the ground part because it never looks great (AGS just doesn't have a proper affine tramsform) and it relies on drawing surface which is quite slow.

Hmm, that's a shame, I think it looks pretty okay for the low res in the examples that I've seen, about how old games of this style looked like in 90-ies.

About speeding this up, in it might be possible to add sprite rotation around x/y axes too, then this will be done very fast by Direct3D/OpenGL.
But my concern here is about texture z-sort, whether it will get in a way or not (because if texture is rotated "away" from the viewer, theoretically it "collides" with any textures behind it).

Quote from: eri0o on Wed 28/08/2024 10:48:54The mode7 world is to be treated as a render area and your game logic should also deal with yanking and putting objects on it as necessary.

That may be true, but I think the module still has to cull the objects that go behind the "camera", marking them as not visible.
SMF spam blocked by CleanTalk