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 - fernewelten

#1
Quote from: Wiggy on Sun 07/08/2022 05:53:08
… undocumented feature in chapter 2 …

Oops. I'd better fix this.

It make take a little while though: That particular adventure was written in AGS4, and last time I looked, AGS4 had become a bit shaky in several areas. AGS4 is still in alpha after all. I'm confident that this is only a transient phase.

As soon as I turn up a version that's stable enough to be used for a game again, I'll port my game to it and remove the bug undocumented feature. This'll also be the time where I will roll together Chapter 1 and Chapter 2 into one unified game.

Quote from: Wiggy on Sun 07/08/2022 05:53:08
Great game though.

Thanks for the compliment!
#2
Quote from: heltenjon on Mon 08/08/2022 11:25:56
The way I solved it: (snip)
No, I'm not proud, but anything to escape those robots!

I think your solution is fine. It does the job for problems of this size, and you can even see it as a preliminary version of the “Chinese” solution: The only main thing that the “Chinese” add is that they optimize the brute-force approach more by going about it iteratively instead of doing just one step, as it were. So it's a very good solution considering that you didn't know the approach and re-engineered it.


#3
Quote from: heltenjon on Mon 08/08/2022 03:21:03
The code for the door had me stumped for a long while, and when I saw the system, I didn't remember how to calculate such an equation, so I half-brute-forced it.

Thanks for the feedback!

As concerns the door lock puzzle, I had some concerns myself. But TBH I'm running out of ideas for lock puzzles, so despite some misgivings I put it into the game. The official term is “Chinese Remainders” puzzle. I don't think that Wikipedia does a good job of explaining how to solve it, so I'll put an algo for it here:

Let's say we look for a number that

  • yields a remainder of 2 when divided by 5
  • yields a remainder of 3 when divided by 7
  • and yields a remainder of 5 when divided by 8.

Spoiler
1. Let's take the first remainder first. A great number that will yield a remainder of 2 when divided by 5 is the number 2 itself, so let's take 2 as the starting point.

2. Note that you can add 5 to the starting point as often as you like, the remainder when dividing by 5 won't change (of course). And that's good because that remainder is already perfect and we don't want to spoil that.

3. But each time you add 5, the remainder when dividing by 7 will change. So let's repeatedly add 5 to the starting point until the remainder when dividing by 7 is exactly right. We arrive at 17, let's call that the new starting point. Now the remainder when dividing by 5 is fine, and the remainder when dividing by 7 is fine, too. Let's keep both fine from now on.

4. Note that you can add 35 (or 5 times 7) to the new starting point as often as you like: The remainder when dividing by 5 won't change, and the remainder when dividing by 7 won't change either.

5. But the remainder when dividing by 8 will change each time. So let's add 35 to the new starting point until that remainder is exactly right. We end up with 157.

6. In this case, this number will work and is a solution to the puzzle. However, the keypad for entering the number doesn't have a zero. So what do we do in cases where we end up with a number that contains a zero? In this case, we can add 280 (or 5 times 7 times 8) to it as often as we like, it won't change the remainders when dividing by 5, by 7, or by 8. We'll do that until we arrive at a number that doesn't contain zeros.
[close]
#4
Quote from: Wiggy on Tue 02/08/2022 15:03:31
how to make diabolically hard puzzles?

So far, I've used classic puzzle dependency charts (Grumpy Gamer) to construct the riddle sequences. So I start at the end, e.g., Ego needs to be prevented from passing through a door. What might get him stumped? Let's lock the door with a combination lock and let's put shards in front of the door. What might prevent Ego from simply pulling out the shards? What would make it difficult to get at the combination of the lock? …

I often “look around”, as it were, and let myself be inspired by the specific mundane circumstances that I already have. For instance, in “Thinker”,
Spoiler
I already have Ego being a robot without a voice module. How can I get a puzzle out of that? Ego would have to be stumped by a task that can only be done with a voice. A task that requires hollering. And that's how I arrived at the puzzle in the vending machine room.
[close]

It's dicey. I don't want to pose puzzles that are so illogical that they can't be done. OTOH, I don't want to pose puzzles that are insanely easy. I'm not strictly against “chores”. OTOH, I don't want to exhaust the player and give them headaches with overly tedious chores.

I find that hard to get right. I'm still learning and experimenting.


#5
Quote from: sthomannch on Sun 31/07/2022 12:48:27
Very interesting, but a bit short.

Sorry. It took me a lot of time to get all those state machines interplaying correctly. When I find the time, I'll add some content for the locker room, making Ian work to turn up a hazmat suit. That'd also have to explain how Ian can't turn up an ID card in there. :)

Thanks for the feedback!
#6
Make the walking area of the room a small band (wide but only a few pixels high) â€" would this work for you? Ego will be forced to go either left or right because there isn't the vertical space in the band to allow other moves.

I don't know with how little vertical space you can get away with. A band only one pixel high might be too risky: If Ego gets off the band, e.g., while turning, they will freeze in place and not be able to move at all.
#7
I think I've seen an animation done exclusively from oil paintings.
There are complete commercial adventures made from clay figures and clay environments (The dream machine). Or from paper cutouts.
So oil painting an adventure, that's doable in principle.

You'd paint your canvasses and then scan them in in some way. If your paintings are A4 or smaller, a fairly standard scanner will do. If you paint A3, well, A3 scanners do exist, but AFAIK most of them are aimed at office environments; getting an A3 scanner that is good for art might be pricey.

It's hard to grasp what makes a game that is “worth it”. The art is only one aspect of adventures. There's also the story and the world building and memorable characters and the puzzles and the minigames and the background music and perhaps the voice acting etc.

It's even harder to forecast the “worthiness” of a game before it has been done. 

Your paintings might be something that makes your adventure special and lets it win art prizes and competitions and charm the players. You might perhaps even get money from artsy European (French?) institutions for it. Whether this will actually happen, well, nobody will be able to say.
#8
Many thanks for the very fast response!

I've finished my room using your workaround which worked fine. I'll have time to look at the temp build in the next few days.
#9
Quote from: fernewelten on Thu 14/07/2022 15:12:36
[H]opefully, I will be able to present a second room after the vote has ended.

And now I've done it. The game sports TWICE the rooms (i.e., it used to have one room, now it has a whooping two of them.) :-D 

After Ian has escaped the Park Deck, he flees to a technical space outside the tomato factory. Lots of trolleys leave that factory and head either for a freight terminal or a recycling facility, as the case may be. How can Ian use that to his advantage?

Includes two additional mini games.

Get the game from itch and tell me how far you come with the puzzles.



#10
Update: "Flight from the robots" has gotten an all-new chapter 2.
Download it here from itch: https://loquimur.itch.io/flight-from-the-robots-2#download
#11
Hi folks, I'm stumped.

(I'm on AGS 3.5.1, newest official build 3.5.1.19, on Windows 10).

I'm managing a handful of trolleys that run on tracks across a room. The trolleys are called “crate” internally and the tracks “conveyor” because I've been stupid and English isn't my mother tongue.  :)

So here's how I define my trolleys:
Code: ags
struct tyCrate
{
    Object *Object;
    DynamicSprite *DS;
};

#define CrateCount 10
tyCrate Crate[CrateCount];

function room_Load()
{
    Crate[0].Object = oCrate0;
    Crate[1].Object = oCrate1;
    …
    Crate[9].Object = oCrate9;
    for (int crate = 0; crate < CrateCount; crate++)
    {
        Crate[crate].Object.Visible = false;
        Crate[crate].DS = null;
    }
}


Whenever a trolley must enter the scene, I select an unused one out of the pool above (parameter crate), let it look like a certain sprite (parameter gr) and initialize it as follows:
Code: ags

#define Crate_DispatchX 1300
#define LowerConveyerY 540
function Crate_Assign(int crate, int gr)
{
    Crate[crate].DS = DynamicSprite.CreateFromExistingSprite(gr);
    Crate[crate].Object.Graphic = Crate[crate].DS.Graphic;
    Crate[crate].Object.Visible = true;
    
    Crate[crate].Object.StopMoving();
    Crate[crate].Object.X = Crate_DispatchX;
    Crate[crate].Object.Y = LowerConveyerY;
}


I have to do it this way because the sprite will be modified while it is "on stage". In particular, a printer will write a checkmark onto its side:

Code: ags
function Printer_PrintLabel(int crate, int gr)
{
    DynamicSprite *ds = Crate[crate].DS;
    DrawingSurface *dsu = ds.GetDrawingSurface();
    dsu.DrawImage(23, 20, gr, true);
    dsu.Release();
}


When the trolleys run off-stage, I dismantle them and return them into the pool, as follows:
Code: ags

function Crate_Destroy(int crate)
{
    Crate[crate].Object.StopMoving();
    Crate[crate].Object.Graphic = kGr_CrateEmpty;
    Crate[crate].Object.Visible = false;
    if (Crate[crate].DS != null)
    {
        Crate[crate].DS.Delete();
    }
    Crate[crate].DS = null;
}


So the dynamic sprite is duly deleted â€" this means when I call  Crate_Assign() again later on, it is extremely likely that DynamicSprite.CreateFromExistingSprite() will return this newly deleted sprite.

And there's my problem: When Crate_Assign() is called the second time around, the object shows a sprite that still has the check mark from the first time written on it. So does that mean that the dynamic sprite hasn't been set correctly? Seemingly not, because the trolley goes into the printer, and when the printer does its thing, shwoosh, suddenly the correct sprite for the trolley shows up at this point. 

I haven't done that much with dynamic sprites so far, so this might be a coding error. OTOH, I might have uncovered an Engine bug, too.

Do you have any pointers where the coding has gone awry?

Find the project with the code here. The original has become quite large; I've pared down the files as much as possible. Everything is in room 6.

To demonstrate the problem:
1. Hit F5 to compile and start the game
2. Left-click on the rightmost "cinorq" (the one above the champagne bottle).
3. Right-click on the rightmost "cinorq".
4. Observe how two different trolleys make their way across the room, ultimately disappearing in the tunnel on the left-hand side.
5. Wait 5 seconds to make sure that the trolleys have been dismantled.
6. Now do it the other way round: RIGHT-click on the rightmost "cinorq", then LEFT-click on the rightmost "cinorq".
7. Observe that the trolleys that emerge still have their printed marks on them. Also, they still look like the ones created in 2. and 3., not the other way round.
8. In the printing station, however, they suddenly switch to the correct graphics.
#12
Critics' Lounge / Re: New AGS logo
Fri 15/07/2022 02:31:50
Hm. At first, I could only see AGS coding as being fuelled by coffee. But on second thought, AGS is an originally British endeavour, so a tea cup would be just my cup of tea (provided it is filled with English breakfast tea and a bit of milk).
#13
Unfortunately, I only have one tiny room to show for this year's; but, hopefully, I will be able to present a second room after the vote has ended.
#14
Quote from: bertolino on Mon 06/06/2022 02:35:49
please help me find glasses

1. Right-click on the places that you suspect to contain the glasses. Eeric, the ghost, needs to look for the glasses. If you left-click instead of right-clicking, you make Eeric try to interact with the hotspots and objects instead of making him look at them. Eeric is so weak that he can't interact with a lot places of interest. So if you left-click on those places, you get a “I can't do that” reaction from Eeric even if the glasses are actually there.

2. So here are some hints on where to look specifically:
Spoiler
They are low.
[close]
Spoiler
They are behind thin panes of wood.
[close]
Spoiler
They are behind the third door of the cupboard that contains all those books, covered partially.
[close]
Spoiler
Hover with the mouse over the rightmost door of the cupboard with the books, any you'll find that Eeric can see through the thin wooden door: it fades away. The upper compartment of the space contains the glasses, behind, I think, a large beaker with pens in it. Right-click on the glasses.
[close]
#15
Hi folks,

A short heads-up: AdventureJam 2022 is about to start on itch.io â€" not Gamejolt as in preceding years â€" over here: https://itch.io/jam/adventure-jam-2022 .

It seems that more than 700 participants have signed up already, although I doubt that several hundreds of entries will come about at the end to be voted on.

If you can come up with a flight-themed adventure (and, of course, if you write your adventure in AGS), you could perhaps double-dip and place it as an entry to MAGS June, too.
#16
In the meantime, you might be able to use the CallRoomScript() mechanism as a "poor man's function pointer".

Your rooms would have an on_call() event such as the following:
Code: ags

function on_call(int fp)
{
    switch (fp)
    {
    case 1; foo(); return;
    case 2: bar(); return;
    case 3: baz(); return;
    case 4: bork(); return;
    …
    }
}


Instead of keeping and passing around a function pointer variable that might point to foo() or baz(), as the case may be, you keep and pass around an integer variable that contains 1 or 3, respectively.

Let's say the integer variable is called MyPoorMansFunctionPointerInt. Instead of calling the function pointer, you'd go CallRoomScript(MyPoorMansFunctionPointerInt);. This would end up as a call to on_call(MyPoorMansFunctionPointerInt) of the respective room and its switch would convert this to a call to the corresponding function.


This is especially interesting if you are writing a module. You want to call a function whenever something happens, but you don't know what functions your module users want you to call. It should work for any function that the module user may want to configure. And the module users should not need to touch your module code in order to configure the function to call.

Well, make a global variable that is externally visible and call CallRoomScript() with that variable. Your module users can set this variable, e.g., in game_start() of GlobalScript.asc and then catch your calls in the on_call event of their rooms.

For instance, your module might have a global variable ThingyGoesBoom that the module calls whenever this has happened.
In module.ASH
Code: ags

import int ThingyGoesBoom;

In module.ASC
Code: ags

int ThingyGoesBoom;
export ThingyGoesBoom;


Whenever the module determines that the thingy has gone boom, it calls CallRoomScript(ThingyGoesBoom);

A module user might have a function CallTheUndertakers(). They want this function to be called whenever the thingy has gone boom. They code:
GlobalScript.ASC
Code: ags

function CallTheUndertakers()
{
    …
}
…
function game_start()
{
    ThingyGoesBoom = 15;
}


(The specific number 15 in the code above is arbitrary but should, of course, be unique for this purpose.)

In the rooms, near the end of the room code
Code: ags

function on_call(int fp)
{
    if (fp == 15)
        return CallTheUndertakers();
}


This might feel as a bit of a kludge, but it works.
#17
Engine Development / Re: Why is AGS slow?
Sat 16/04/2022 20:16:02
Quote from: Crimson Wizard on Sat 16/04/2022 19:25:38
What fernewelten is refering to, probably, is of is checks that are done by the script interpreter during certain bytecode operations. Like - stack operations. They probably happen all the time, for every little move on the stack (I may be mistaken here, because some were disabled in the past).

Yes, that's the gist of what I was trying to say. When the script interpreter is told to float-add two things, for example, it doesn't just do just that, it checks beforehand whether the respective register etc. has been loaded, whether a value of type 'float' has been written into it, and so on. That's good for checking whether the Compiler has done its thing correctly. That mechanism is bound to have discovered lots of bugs by this time.

But the trouble is, if the Compiler has done the job properly that it was programmed for, then those checks might still be done thousands of times at runtime, over and over again, even for the same source code statement and the same block of Bytecode, when it needn't have been done at all.

There are certain things that the Compiler can't check, for instance whether a pointer location that needs to be dereferenced contains a null pointer at runtime. It's sensible to make the script interpreter check these things. The Compiler may even tell the script interpreter to check something specific, by issuing a suitable Bytecode instruction.

On the other hand, there are a lot of things that the Compiler could make sure of, given our AGS language. For instance, it should be able to make sure that a memory location that is supposed to contain a float hasn't been loaded with an int instead. In these cases, a lot of time can be saved by letting the Compiler do its thing, and telling the interpreter: “Look, Buster, Master has told you that these two things are floats and commanded you to add them, so just obey right now without wasting Master's time!”

It would mean requiring that every Bytecode that is given to the Engine has been produced by the Compiler, or at least, that if you do give "hand-written assembly Bytecode" to the Engine, side-stepping the Compiler, you do it at your own risk.
#18
Engine Development / Re: Why is AGS slow?
Sat 16/04/2022 16:54:55
As far as I know, the Engine does much more than the microprocessor would do that the Engine has been based on. Mainly, double checks, e.g., that the registers contain the type of data that they're supposed to. Seen from this vantage point, the script runner is a “debugging” script runner, only the “debugging” runs all the time no matter whether the code is still debugged.

There might be two angles to speed this up:

  • Make the script runner trust the Compiler. The Compiler only runs once whereas the script runner runs each time. So when the script runner abstains from checking those things that the compiler should have already made sure of, then this can potentially cut out a lot of superfluous runtime activity.
  • Provide a sleek “production”  script runner that only does the barest necessary, side-to-side to the current script runner. Some kind of flag would determine whether the code is run with the careful current runner or with the fast “production” runner. Those users that need a very fast engine could set the flag appropriately and forego the checks. Or else we could set up things as follows: When the game is started with F5 then the engine uses the careful current script runner, when it is compiled with F7 and run then the engine uses the sleek “production” script runner.
#19
You're great! Many, many thanks for the fast response. It hadn't occurred to me that the line that specifies the encoding needs to be commented out.
#20
So, I've got an AGS4 game that has the usual ANSI (ASCII) code files.
It features room 1 having the code:
Code: ags

function room_AfterFadeIn()
{
    […]
    player.Say("Vamp squawks: Say hello to Felix or Jody!");
}


The corresponding trs file has the name DE-de.trs:
Code: ags

// AGS TRANSLATION SOURCE FILE
// […]
// 
// ** Translation settings are below
// ** Leave them as "DEFAULT" to use the game settings
// The normal font to use - DEFAULT or font number
//#NormalFont=DEFAULT
// The speech font to use - DEFAULT or font number
//#SpeechFont=DEFAULT
// Text direction - DEFAULT, LEFT or RIGHT
//#TextDirection=DEFAULT
// Text encoding hint - ASCII or UTF-8
#Encoding=UTF-8
[…]
Vamp squawks: Say hello to Felix or Jody!
Vamp quäkt: Grüß Felix bzw. Jody schön!


... and is in UTF-8 without BOM, as far as I can ascertain.

When I compile the translation and run the program, it seems that the Engine:

  • does find the translation of the sentence,
  • but does not convert its UTF-8 combinations properly into multibyte characters:



So what am I missing so that UTF-8 translation files work and display umlauts correctly?
The game is here: https://www.loquimur-online.de/ags/talk/2022-04-07/Test.zip
SMF spam blocked by CleanTalk