Related to "the future of AGS" -- but only for OCD and nerds

Started by Monsieur OUXX, Tue 17/01/2012 17:53:13

Previous topic - Next topic

Wyz

Instead of having a set of behaviours you could also infer this from other states. Let's say we keep track of the location the character is in, we could have conditionals for each script section. So if a event occurs it will check for each script assigned to that script if the condition holds, if it does it will be executed, if not it will be skipped.

Let's say we have a NPC that moves along with the player character. We could have something like this (very much in pseudo code):
Code: ags

void give_lotion()
{
  player.Say("Hey, could you do my back?");
  // ...
}

(...)

npc1.AddEvent('give', give_lotion, player.location == 'beach' && player.holding == 'lotion')



That way the whole inheritance principle will still work, yet it will imho be easier to maintain. You would have to deal with the order of scripts, and one script might stop the remaining scripts to be executes (stop propagating). You can even allow closures on script, an example (again very much pseudo code):

Let's say we have a npc that hates everything:
Code: ags

void hate(String thing)
{
  npc2.Say(String.Format("I hate %s!", thing))
}

npc1.AddEvent('give', hate(player.holding), true)


Just some ideas :D
Life is like an adventure without the pixel hunts.

Monsieur OUXX

#41
@Wyz:
I'm not sure which of those mechanisms is the correct interpretation of what you described :

1. You call "AddEvent" once and for all for a given object (e.g. "npc1"), and then the script given in parameter (e.g. "give_lotion") will be executed every time the event (e.g. "give") is fired. That means the engine computes the objects list that are supposed to react to "give".
2. You call "AddEvent" manually every time you need the event to be caught and propagated -- pretty much like a more elegant and a more practical funciton call.

===

If the correct interpretation is 1, then :

What you're describing is pretty much the concept of Listener, which is actually present in most OOPs, and especially in Flash's Actionscript or Flex (again!).
You introduced a concept of propagation condition, that would itself rely on what we could call "context".

I'm not a fan of the context being defined beforehand (in the listener's instanciation), because that means the condition (e.g. "player.location == 'beach' && player.holding == 'lotion'") will need to be stored somewhere, and then parsed and interpreted at runtime. Technically that brings the engine to a whole new level! Ouch!

I'm more in favor of something simple like it is currently done in AGS :
- The engine detects when an event is fired -- either internally (e.g. "the animation finished"), or because the scripter fired it manually (e.g. "myLoop.StopPlay"), or because of user action (e.g. "click")
- It fires the corresponding function

What I suggest our engine brings is:
a. The ability to define with more accuracy (and dynamically) all the places in which the event can be intercepted (in the global "onClick function" or in any "Behaviours" the scripter wishes to define). This can be done with something similar to what you described, a listener ( npc1.AddEvent("give", give_lotion) ).
b. The conditions are then checked internally to the fired function (e.g inside of "give_lotion: "if player.location == beach"). That's where I differ from your solution: This way we stick to regular scripting and get rid of the need for conditions parsed at runtime.
c. Still within the function, we decide if the event should be propagated to the underlying event handler (e.g. "parentBehaviour.propagateEvent()" ). This would be similar to the way constructors can be overridden or "extended" in some OOP languages like Java (The well-known "super()" function).

===

Yesterday I had that thought that Behaviours could be extended to Rooms (we could keep the concept, except we could rename them "ambiance" or "context"). A "room behaviour" or "context" or "ambiance" would be used as some sort of dynamic template that could store the room's music or anything else that wyou'd like to be shared between several rooms.
Examples:
- The musical atmosphere (relaxed? action scene? a monster appears and the music goes crazy?)
- the characters' reactions (instead of defining them inside global Behaviours and then attaching them to the character at runtime with a line of script, you define attach those behaviours to the room: the "beach behaviour" will be attached to all the "beach" rooms permanently, instead of being attached to the character some of the time).
- The room's shader (night shader, etc.)

This concept of "Room behaviour" (that may contain Character events) brings the need of having a way to define Behaviours precedence (if the same event is defined int he Character's Behaviour(s) and in the Room's Behaviour(s), which one has the priority?)

===

Here is apseudo-code that would summarize ALL the concepts I've dealt with above and in previous posts.
I want to stress that ALL of this script can be translated into easily-readable and editable Editor items (see my previous mockup)

Code: ags

behaviour EveryCharactersBehaviour {  	//if you attach this behaviour to something
										//else than a Character, the event "FinishedWalking"
										///will probably never be fired
	onevent FinishedWalking(character) {
		character.Say("I just finished walking");
	}
	
	onevent ... {	//another event
		...
	}
}

behaviour MainCharacterBehaviour extends EveryCharactersBehaviour { //inherits my generic (yet custom) Characters behaviour
	onevent FinishedWalking(character) {
		super(); 	//you tell the script to also run the inherited behaviour!
					//Therefore, The character will say "I just finished walking"
		character.Say("...And I'm the hero!"); //only cEgo will say that after finishing walking
	}

behaviour NPCBehaviour extends EveryCharactersBehaviour { //inherits my generic (yet custom) Characters behaviour
	onevent FinishedWalking(character)	overrides BeachRoomBehaviour, CityRoomBehaviour {
		//the line above tells the engine that if the same event ("FinishedWalking")
		//is also defined in the listed room behaviours (BeachRoomBehaviour, CityRoomBehaviour),
		//then ONLY THIS FinishedWalking will be run. Otherwise it ALSO fire the FinishedWalking
		//of the room behaviours. If the room behaviours contain "overrides NPCBehaviour", then
		//ONLY THEIR FinishedWalking will be run
		
		
		//"super()" is never called, so the character will NOT say "I just finished walking"
		
		character.Say("...And I'm an NPC!"); //only the NPCs will say that after finishing walking
		
		//the line below shows how you check some conditions INSIDE the event's function
		//rather than defining them beforehand like in Wyz's model
		if (character.walkarea == "sand") { character.Say("...And I'm standing on the sand!"); }
	}

behaviour EveryRoomBehaviour {
	onevent FinishedWalking(character) {
		character.Say("this room looks pretty generic to me!");
	}
}
	
behaviour BeachRoomBehaviour extends EveryRoomBehaviour{
	onevent FinishedWalking(character) {
		//"super()" is never called, so the character will NOT say "this room looks pretty generic to me!"
		character.Say("If I were an NPC, you'd never hear me say that!"); //because BeachRoomBehaviour is overridden in NPCBehaviour
	}
}

behaviour CityRoomBehaviour extends EveryRoomBehaviour{
	onevent FinishedWalking(character) {
		super(); //super() is  called, so the character will say "this room looks pretty generic to me!"
		
		character.Say("...except it's clearly in a city!"); //an NPC will  not say that because CityRoomBehaviour is overridden in NPCBehaviour
	}
}

behaviour UnderWaterRoomBehaviour extends EveryRoomBehaviour{
	onevent FinishedWalking(character) {
		super(); //super() is  called, so the character will say "this room looks pretty generic to me!"
		
		character.Say("...Blblbbllb"); //EVERY Character will say that because UnderWaterRoomBehaviour is NOT overridden in NPCBehaviour and MainCharacterBehaviour
		
		//the line below shows how you check some conditions INSIDE the event's function
		//rather than defining them beforehand like in Wyz's model.
		//this oxygen check will happen in every underwater room
		if (character.oxygenLeft < 0) { character.Say("Oh no I'm dead!"); }
	}
}


...

//ALL THE SCRIPT BELOW CAN BE DONE EITHER DYNAMICALLY IN THE SCRIPT, OR BEFOREHAND IN THE EDITOR:
cEgo.attachBehaviour("MainCharacterBehaviour");
npc1.attachBehaviour("NPCBehaviour");

rooms["MyCityRoom"].attachBehaviour("CityRoomBehaviour");
rooms["myBeachRoom1"].attachBehaviour("BeachRoomBehaviour");
rooms["myBeachRoom2"].attachBehaviour("BeachRoomBehaviour");

rooms["myUnderWaterRoom1"].attachBehaviour("UnderWaterRoomBehaviour");
rooms["myUnderWaterRoom2"].attachBehaviour("UnderWaterRoomBehaviour");

...



Another idea:

Code: ags

//IN THE COURSE OF THE GAME

//Oh noes! The main character has become a zombie/OSD!
//note that I open the possibility of having a "replaceBehaviour" rather than "attachBehaviour".
// "attacheBehaviour" would allow to pile up different behaviours while "replaceBehaviour"
// swaps them.
// Having both "attach" and "replace" would allow to add "aggregation" mechanisms to the "inheritance" mechanisms.
// These are two diferent and complementary approcahes : Search in Google "inheritance versus aggregation"

cEgo.replaceBehaviour("NPCBehaviour");




Another other idea ;)
I'm not dropping the events handling at "event level" rather than "behaviour level", like Wyz suggested. I just didn't detail it.
Both could be complementary.

Code: ags

npc1.AddEvent('give', give_lotion);

 

RickJ

I like the way script modules deal with events and believe it's similar to listeners.  For example, one or more module scripts and the global script can have mouse event handlers.   Event handlers are executed in the same order as the scripts appear in the AGS editor.  Any of the event handlers may prevent further propagation by executing the ClaimEvent() function.  

Now lets suppose we expanded upon this concept.  Imagine that rooms, objects, characters, guis, etc are all sub-classes of entity where each entity can have it's own script and script modules.  A character for example would have a character script and optional modules similar to the way AGS game script is now organized.

Code: ags

gMyGame
     rSomeRoom
          oSomeObject
          hThisHotspot
          cNpcCharacter
     rSomeOtherRoom


Now suppose it were possible to organize entities in a hierarchical structure as shown above and that the player talks to cNpcCharacter.    The TalkTo event handler in cNpcCharacter is executed first.  If the event handler executes ClaimEvent()  then it is not prograted any further.  If there is no handler defined in cNpcCharacter or it it's handler does  not ClaimEvent() then the event is propagated  to rSomeRoom's event handler.  The event is finally proprogated to the gMygame event handler.

Now here is the cool thing about this.  Suppose cNpcCharacter moves to rSomeOtherRoom.  Now if the player talks to cNpcCharacter the event handlers are executed in this order cNpcCharacter, rSomeOtherRoom, gMyGame rather than cNpcCharacter, rSomeRoom, gMyGame.  The context is autoimatically and dynamically changed at runtime.

I think this also covers agregated and inherited functionality discussed above.

[edit]
spelling

Monsieur OUXX

On the one hand I like how your model gives a formal framework for the precedence of events handling (which event is handled first?). That was the part I was thinking my model wasn't making explicit enough.

On the other hand, I really hate everything else about it (it's not personal, read below  :D)
- it's very static (how do you swap the order at runtime if needed?)
- it's like a whole new hierarchy of stuff, transversal to the existing script and objects hierarchy (where do you define this? in a separate text file?) How does it blend with the rest of the Editor/scripts?
- it generates a lot of code duplication, see the example below :

(if I want the room's event handling to have the priority in Room1, Room2 and Room4 but not in Room3 and Room5...Now imagine if you interleave all the event handling of your initial post into the example below -- the number of required lines goes exponential!)
Code: ags

gMyGame
     rRoom1
          cNpcCharacter
     rRoom2
          cNpcCharacter
     rRoom3
          
     rRoom4
          cNpcCharacter
     rRoom5
        



I'm totally OK to be proven wrong on all this.
 

Wyz

Your posts have made me think about event handling and I'm not sure what I would think is best so instead let me respond to some other aspects.

I think we are generally on the same page about the events but it is down to how the pie gets cut.
I've spotted three ways of how event propagation is handled now:
  • Explicit, by calling the next event from the first one fired (M. OUXX)
  • Implied, user specified (me)
  • Implied by the structure (hierarchy) (RickJ)

    well I think those mentioned are all somewhat quirky in ways, we might be able to do this better. Let's recall how AGS does this currently: every event can be mapped to a function, if it is not mapped there is a single fallback event that gets fired. This behaviour is very useful for adventure games, but we might be able to generalize it a bit more.
    Let's assume we have one big set that contains all possible events in a game called Events. We can immediately name subsets like: player events, room events, screen events, sound events. We can divide it some more: npc1 events, 'lotion' events. We can divide it differently: 'give' events, 'talkto' events, 'interact' events. This said let's see how an user would like to map these events.

    Say a user would like for each character he tries to give the lotion this person would respond the same.
    Code: ags
    
    function default_give_lotion()
    {
      this.Say("Get lost freak!")
    }
    

    However there is one person, np1, who takes the lotion. (see give_lotion)
    So what would be convenient? There are two ways that immediately pop up in my head:

    1) Let each event map only one function, that way you can first map 'default_give_lotion' to the Events subset 'give' and afterwards you replace np1's 'give' event with 'give_lotion'.

    2) We allow every event to have an ordered list of functions, the first one will be executed regardless. When this one finishes the next will be called, unless the first one asks for a propagation stop 'ClaimEvent', etc.
    Now we simply add 'default_give_lotion' to the 'give' events, and 'give_lotion' to np1's 'give' event.
    Next we let 'give_lotion' call 'ClaimEvent'.
    The only think that is left to do is make sure the order is right. Either we directly order the event list or something a bit more genius: you can actually order the functions themselves by simply letting the users say: 'this function is more important then that one'. It works really intuitive if you'd ask me, but I might be wrong so discuss!  :D

    Well, I only named two methods however there are infinite possible methods, but this gives you something to think about. :)
Life is like an adventure without the pixel hunts.

RickJ

@Monsieur OUXX:  No, no, no you you are missing the cool part of this.  The structure is static at design time and changes dynamically at runtime.   So the following only means that rSomeRoom has cNpcCharacter when the game is first started.  
Code: ags

gMyGame
     rSomeRoom
          oSomeObject
          hThisHotspot
          cNpcCharacter
     rSomeOtherRoom


However, when cNpcCharacter moves to rSomeOtherRoom we can then say that rSomeRoom no longer has cNpcCharacter and that now rSomeOtherRomm has cNpcCharacter like this.
Code: ags

gMyGame
     rSomeRoom
          oSomeObject
          hThisHotspot
     rSomeOtherRoom
          cNpcCharacter


Now consider what happens when there is an inventory item, iSomeItem, and the player character in rSomeRoom.  We can say that rSomeRoom has iSomeItem and cPlayerCharacter like this.
Code: ags

gMyGame
     rSomeRoom
          iSomeItem
          oSomeObject
          hThisHotspot
          cPlayerCharacter
     rSomeOtherRoom
          cNpcCharacter


Now suppose the player clicks PickUp on iSomeItem.  The event handlers are executed in the order iSomeItem, rSomeRoom, gMygame.  Further lets suppose one of the handlers moves iSomeItem from rSomeRoom to cPlayerCharacter.  So now we would have the following structure.  
Code: ags

gMyGame
     rSomeRoom
          oSomeObject
          hThisHotspot
          cPlayerCharacter
               iSomeItem
     rSomeOtherRoom
          cNpcCharacter


Now clicking on iSomeItem would execute event handlers in the order iSomeItem, cPlayerCharacter, rSomeRoom, gMyGame.  

If cPlayerCharacter moves to rSomeOtherRoom the rSomeOtherRoom would have cPlayerCharacter which would have iSomeItem like this.
Code: ags

gMyGame
     rSomeRoom
          oSomeObject
          hThisHotspot
     rSomeOtherRoom
          cNpcCharacter
          cPlayerCharacter
               iSomeItem


Now event handlers execute in the order iSomeItem, cPlayerCharacter, rSomeOtherRoom, gMyGame.   And if the player dropped the item in the current room then we would have the followoing and event handlers would execute in the order iSomeItem, rSomeOtherRoom, gMyGame
Code: ags

gMyGame
     rSomeRoom
          oSomeObject
          hThisHotspot
          cPlayerCharacter
               iSomeItem
     rSomeOtherRoom
          cNpcCharacter
          cPlayerCharacter
          iSomeItem


@Wyz:  I agree with you that the three of us are trying to achieve the same ends but and are struggling to find a simple and elegant means.  After reading your comments and thinking through the above example some more things or quirks come to mind.  

When you were talking about categorizing events it occurred to me that  one event could have a number of interpretations.  For example, a LookAt cursor click on iSomeItem could be interpreted as "LookAt iSomeItem", "LookAt any item", "LookAt anything", "Mouse Click", "?".    It seems to me none of us have addressed or even mentioned this situation.  I think generally what happens now is that we get a generic LookAt event and then in the event handler we can lookup what is under the cursor and then use a giant if-else thing to decode the event to whatever degree of specificity  is required.  It would be nice if we could come up with something better.

I haven't given this a lot of thought but to move the discussion along perhaps I can pickup from what Wyz said about classifications.  
Quote
Let's assume we have one big set that contains all possible events in a game called Events. We can immediately name subsets like: player events, room events, screen events, sound events. We can divide it some more: npc1 events, 'lotion' events. We can divide it differently: 'give' events, 'talkto' events, 'interact' events. This said let's see how an user would like to map these event
.  

Perhaps it's all in how we name events?  Suppose events were named in a hierarchical manner, from general to specific.  So to answer my own question we would have the following possible interpretations of a mouse click on iSomeItem

  • Mouse
  • Mouse.LookAt
  • Mouse.LookAt.Item
  • Mouse.LookAt.Item.iSomeItem

    Event handler functions could be declared something like the following. All of these would respond to a the LookAt iSomeItem event.  The handle keyword informs the compiler that these are event handlers so that it can use the function name to connect the function to the actual events.  This would negate the need for all that pointy and clicky stuff and would mean that events could be added or removed simply by cutting or pasting  snippets of script code.  The pointy and clicky stuff could be retained but would only be a wizardly thing that pastes an appropriate template into the script.

    Code: ags
    
    handle Mouse(button id, entity type, entity id) {
    }
    
    handle Mouse.LookAt(entity type, entity id) {
    }
    
    handle Mouse.LookAt.iSomeItem(ntity type, entity id) {
    }
    


    There was one other issue I had with my first examples and I am not sure of the correct answer.  In the case of events such as LookAt, PickUp, etc that are actions being performed by the player character, I wonder if the player character's script should have first crack at handling the events?   If the answer is yes then when there is a LookAt iSomeItem when player character has iSomeItem the event will be propagated to cPlayerCharacter twice.  What to do about that?  

    [edit]
    Monsieur OUXX I hate you for making this interesting thread and I hate Ryan Timothy for making all the pretty pictures!  Damm you guys!  :'(  ;D   No, just kidding of course.  

    Here is a document I made over the last few days and thought you (MX, RT, WZX, et al) may enjoy looking at.  I'm sorry for PDF but that was the easiest way to get a web viewable document from my version of Visio.

    http://img28.imageshack.us/img28/9957/entityconcept.pdf

Monsieur OUXX

#46
Bumping

-- so that everybody can enjoy (and discuss) the PDF produced by RickyJ (at the end of his previous post).

EDIT:
WOW! I HAD NO IDEA THERE WAS SO MUCH MATERIAL PRODUCED, AND SEXY WITH THAT! (I opened the file only after mentionning it)
RickJ, I hope to be able to comment on that ASAP.
 

Ryan Timothy B

I have no idea where to begin with commenting on Rick's PDF of ideas but it's pretty close to how I would like to see the future AGS. I hope to comment more on it some other day when I have more time.


But for now I have been thinking of scripting and how to allow more advanced players to get under the hood.

For example, where Character.Say is a standard function with AGS, you could actually create your own Character.Say function that overrides the default AGS function like so:
Code: ags

public override void Say(this Character c)
{
  //whatever we now want Say to be instead
}


The only tricky part is how to decide which script is the master script that gets the override. That's assuming you had 2 modules with an override Say function.

What would also be nice is being able to have a partial function for other purposes.
Code: ags

public partial void Say(this Character c)
{
  ACharacterHasSaidSomethingThisManyTimes++;
}

etc..

Alan v.Drake

#48
Quote from: Ryan Timothy on Wed 08/02/2012 05:16:06
I have no idea where to begin with commenting on Rick's PDF of ideas but it's pretty close to how I would like to see the future AGS. I hope to comment more on it some other day when I have more time.


But for now I have been thinking of scripting and how to allow more advanced players to get under the hood.

For example, where Character.Say is a standard function with AGS, you could actually create your own Character.Say function that overrides the default AGS function like so:
Code: ags

public override void Say(this Character c)
{
  //whatever we now want Say to be instead
}


The only tricky part is how to decide which script is the master script that gets the override. That's assuming you had 2 modules with an override Say function.

What would also be nice is being able to have a partial function for other purposes.
Code: ags

public partial void Say(this Character c)
{
  ACharacterHasSaidSomethingThisManyTimes++;
}

etc..

I strongly support overrides,there are many cases when we'd need to redefine the default behaviour of functions like Say or Think and it would keep the code clean and compatible.


- Alan

monkey0506

Currently AGS doesn't allow any form of function overloading. Once a function is declared with a specific name within a specific scope then no other functions can take that name, and if the function in question is in the global scope (and declared first) then it's even impossible for scoped/member functions to take on that name. That would be a huge step forward for the engine if we could do this.

However, overloads and overrides would both rely on the compiler, unless you're going to just dynamically regenerate the function names...a bit of regex magic and that could work, but it would be more of a workaround than a fix.

Monsieur OUXX

Quote from: monkey_05_06 on Wed 08/02/2012 16:04:19
Currently AGS doesn't allow any form of function overloading.
overloads and overrides would both rely on the compiler. a bit of regex magic and that could work

It's not about AGS can or can't do, it's more about how things should be shaped (not even the UI nor the script, but just the concepts).
 

Ryan Timothy B

#51
Edit: Rewrote my post and added some more

Script Extension:
If we were to have scripts for each element like Characters, GUIs, Rooms, Inventory Items. We would need a way to access the Public script functions and elements but only ones that are Public.
So I was thinking a "Script" extension for each Class would be most logical.

For instance accessing room script elements:
rBeach.Script.doFunction();
rBeach.Script.thisIsAStruct.count++;

And obviously objects and such should be accessible even if you're not in that room:
rBeach.oDesk.doExtenderFunction();
rBeach.hPainting.doExtenderFunction();

Then for Characters:
cJoe.Script.JoeHasTalkedToBilly = true;
cJoe.Script.doFunction();


Inventory:
That "Display multiple icons for multiple items" shouldn't be the end all option. It should be item specific. If I had the item Coins, I wouldn't want multiple coins in my inventory. But if I had the item Mug, I may want it to show every mug I have, just like in Monkey Island.

One possiblity is to have inventory items like a Class. So if you had 3 Mugs, they all have their own copy of the variables and such from within the script. So iMug[0].Script.Health  would be different from iMug[5].Script.Health  (Health being an Integer variable)

So that could be one way you manage inventory items better. Where an inventory item 'could' have a repeatedly execute always, if needed. Then you could count down the Health of each Mug as the Grog in it eats away at it. Changing the graphic of the Mug item.


Obviously the Script extender wouldn't be accessible to the ID class. Character[1].Script  wouldn't be allowed. Since accessing the functions and elements for a character would only need to be done for that individual object only.
You'd just use the main script for each element type to make an extender function, or struct, or whatever that would work for all objects.

Ryan Timothy B

Actually the same could apply for characters and their master script. Since all character share the master script but also have the ability to have individual scripts, the master script could act as the additional Class for each character.

So you could then assign Variables and such to all characters in the master script.

cJoe.Script.TalkedThisManyTimes  !=  cBob.Script.TalkedThisManyTimes

That's one way to sorta add to the Character Class. Either that, or you could just allow Partial to work with the Character Class.

Crimson Wizard

Don't know where to put this... while there are lots of discussions regarding theory of making adventure games, it does not look like there's any discussion held regarding making game framework anymore.
Just wanted to share little thoughts I had in the past, hopefully adding my "2 cents" to this thread.

Localization viewed as generic asset substitution

There was time when I thought localization is all about translating text resources, and the program simply needs a dictionary that would find a string by some key - either numeric ID or other string (usually - text in original language). But it appeared to be not true: sometimes you need to change other resources too, such as sounds (if there's a voice recorded) or images (if it contains text). Also, when translating UI texts you may meet a need to change layout of elements inside window, because translation does not fit well in the original one.
If one will go further, he will naturally come to a thought, that every asset in the game may be viewed as a target for localization. The question is only in what is defined an "asset". If the hypothetical "framework" we are observing has literally everything inheriting some basic type and its properties, then everything is an asset, which may contain other assets, forming a tree like structure. Imagine now, that you can create localization as a set of rules which replace only certain assets in this tree.

What does it give to us? Obvious thing is replacing strings and speech audio clips. But there are special cases when replacing string by string does not solve a problem. What if there's a joke that sounds well in one language and does not in another? Game developer / translator may find a substitution that requires slightly different staging: two lines instead of one, adding certain animation to emphasize the meaning, and so forth. In most difficult case devs could be unable to find proper translation, and if the joke is not an important part of a story would not it be simplier to cut the joke from certain localization completely? In such case we may need to deal with game script. Now, what if the script is an asset, and a part of the script (the function, event handler) that plays a joke is an sub-asset of the script: the localization rules may replace the part of the script or completely disable it.

Considering further, is the localization the only reason why we may want to change something in the game? Of course, there may be a need to change controls and GUI depending on platform you run the game on. Or difficulty setting, disabling certain puzzles.
Finally we come to concept that localization itself is just a sub-type of the generic asset substitution: a set of rules that define how game resources are replaced by some switch or a combination of switches.
Instead of writing zillions of conditions in the script, for example, such as "if language is Spanish, then replace the image on the wall" or "in case of legal German localization remove the blood from animation", it is better to have some kind of "configuration" rule for the game.
In the tree of assets we could have another layer per every configuration, possibly inheriting each other, that add, remove or replace assets in the parent layer they inherit.


Monsieur OUXX

Quote from: Crimson Wizard on Fri 28/02/2014 12:17:18
it is better to have some kind of "configuration" rule for the game. In the tree of assets we could have another layer per every configuration, possibly inheriting each other, that add, remove or replace assets in the parent layer they inherit.

I understand perfectly what you mean. Did you just come up with that, or do you think this theory has been explained and studied somewhere else before? That might typically be the kind of thing that has a name, and also has some implementations somewhere out there provided you know its name.
 

Crimson Wizard

Quote from: Monsieur OUXX on Fri 28/02/2014 23:09:37
Quote from: Crimson Wizard on Fri 28/02/2014 12:17:18
it is better to have some kind of "configuration" rule for the game. In the tree of assets we could have another layer per every configuration, possibly inheriting each other, that add, remove or replace assets in the parent layer they inherit.

I understand perfectly what you mean. Did you just come up with that, or do you think this theory has been explained and studied somewhere else before? That might typically be the kind of thing that has a name, and also has some implementations somewhere out there provided you know its name.
I can't say the idea is totally mine, you can find this kind of mechanism all around, perhaps not precisely as I depicted here. The virtual functions in class hierarchy, the build configurations which apply extra options on top of "default" ones, etc. The name? hmm... don't know. "Subtyping" and "inheritance" maybe.

Snarky

No offense intended, but in line with my general skepticism towards making everything virtual and abstract and so on, I'm not convinced this asset localization concept is a good idea:

Yes, you could do that, but translation and voice packs already take care of most of the cases; it's pretty easy to write code to switch out some graphics (of which there will probably only be a few that need modifying anyway); and if you're going into modifying animations and things like that, you're going to have to delve into the existing code anyway, so is this really any easier than just putting in a simple branching check?

And even if it does make the job easier (let's say that it cuts down the time to package a translated edition of a full-length game, after all the assets and translation files have been prepared, from six hours to two hours), is the intersection of people who need to produce sophisticated localizations of their games and those who are interested in using AGS as their engine significant? Or are you talking about putting all this effort into a feature that will maybe save a couple of people a few hours of work a handful of times?

The cost â€" beyond just the effort of implementation â€" is adding a layer of complexity in the editor and engine, and introducing this whole notion of abstract assets that can be instantiated by different actual assets in each variation, which is an irrelevant consideration for 99% percent of people using AGS. Meanwhile (and I know I keep going on about this), Unicode support is a pressing need for which there isn't a good workaround, and the lack of it is actually stopping people from producing games in other languages. If we want to address localization, that's the  one feature that really matters.

But of course, this is just my opinion.

Crimson Wizard

#57
Sorry, I did not mean to suggest this for AGS in its current state, I was speaking about hypothetical system. I also did not mean that's a pressing issue.
That was a talk about hypothetical design made from scratch.

Generalization requires to spend much more time on planning, but saves much more time during actual development (and further extension).

Snarky

Ah, in that context it makes more sense, sure. What I seem to remember reading some other game engines do, is that they have all their assets stored in a directory hierarchy on the disk, and then for alternate game versions they just have a copy of that directory hierarchy, with files that override the original if present.

So for example, if my default assets are stored in "My AGS Game/assets", and a particular sprite is "My AGS Game/assets/graphics/rm_street1/sign.png", then its German replacement might be "My AGS Game/alt_versions/German/assets/graphics/rm_street1/sign.png". You could potentially do the same thing with a whole GUI. Any asset not found in the current alternate path would use the default. You wouldn't really need to worry about it in the IDE itself.

As for relatively small changes within a larger script (like adjusting an animation), I still think the most straightforward thing is to just put in a branching path in the code.

SMF spam blocked by CleanTalk