The annotated cellar: an experiment
Above all, I think the whole concept suits itself to be an author''s tool to suggest, guide, and direct NPCs in the world, paricularly when in the presence of the player. The author essentially is given the ability to encode within the environment (think in terms of a movie production set) the necessary information for the actors (the NPCs) to give an interesting performance for the benefit of the player.
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
I have been thinking out and advocating an approach to this in the Annotated objects which drive behavior thread. So I will continue to explore that concept over there provided others don't move the thread in a different direction.
In the meantime, I thought I might flesh out the scenario here since nobody else seems to want to. To make things a little more interesting, I am thinking the cellar should have two rooms: the main cellar and a storeroom. On the wall of the main cellar is a map of Russia. In the storeroom, which is accessed via a door which is closed, is a broom and a chair.
So, we have:
Main cellar room: It has a trapdoor which is closed and latched which leads to the living room. To the east is a closed but unlocked door. It leads to the storeroom.
Storeroom: It has a door to the west which is closed and unlocked which leads to the main cellar room. In the storeroom is a broom and a chair.
___________________________________
Edited by - bishop_pass on January 22, 2002 10:22:25 PM
In the meantime, I thought I might flesh out the scenario here since nobody else seems to want to. To make things a little more interesting, I am thinking the cellar should have two rooms: the main cellar and a storeroom. On the wall of the main cellar is a map of Russia. In the storeroom, which is accessed via a door which is closed, is a broom and a chair.
So, we have:
Main cellar room: It has a trapdoor which is closed and latched which leads to the living room. To the east is a closed but unlocked door. It leads to the storeroom.
Storeroom: It has a door to the west which is closed and unlocked which leads to the main cellar room. In the storeroom is a broom and a chair.
___________________________________
Edited by - bishop_pass on January 22, 2002 10:22:25 PM
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
hope I''m not too late to add to this. This is a cool idea.
I''ll run through an idea of how (purely as a designer trying to put in a helper)he might react first, then think about coding it.
Ideally, I think the sidekick will wait around initially, waiting for the player to explore. This is simply because the sidekick shouldn''t necessarrally decide all the gameplay, or play the entire game for the player. So, for Time Frame 1, The sidekick waits, basically until he gets bored. Boredom should probablly be an internally clocked stat.
Once he''s done with that, the next thing I''d hope for is an a simple explanation of the current goal, and what the large goal is. "We''ve got to get out of here. XYZ is still waiting for us."
Then, the side kick would walk around the room, looking around at the objects of interest to point them out to the player. If it was hard to see at this point, he might do something like turn on a light (things that make the situation more visible, but don''t necessarrally solve the puzzle). Perhaps he''d even point them out in audio "Hey now, what''s this?" "Could this be important?"
If nothing happened still the sidekick start fidgeting around the door to the other room, having now gotten a signal from it that says "Goal is this way". Again, he''d toss out a few more indications that this is the right way to go. "I wonder what''s in here. We should check it out."
And that''s about as far as I''d take it. But let''s continue for the sake of excersize. Having gotten really bored, and fiddled with everything else in the room, he decides to go through the door. Having gone through that, he starts up a similar sequence as in the previous room. While looking around he finds the objects, and again, makes a hint at them being important, "Hey, stuff!"
If still, there is no response from the player, he might annoucne the names or uses, or even bring them into the other room. If the player still doesn''t do anything, The sidekick can fiddle around with the chair and broom a bit, possibly taking the "stand on me" and "poke with me" attributes in concert, and standing on the chair, unlatching the trap door with the broom, and escaping. The side kick then proceeds to clear out the mansion, making great riches, beating the game, and becoming a well renown hero.
That is, of course, why we neeed to be a little careful about them being able to handle everything, then they can play the game without us.
I''ll try to think up ways of coding this up later.
hope this helps out.
I''ll run through an idea of how (purely as a designer trying to put in a helper)he might react first, then think about coding it.
Ideally, I think the sidekick will wait around initially, waiting for the player to explore. This is simply because the sidekick shouldn''t necessarrally decide all the gameplay, or play the entire game for the player. So, for Time Frame 1, The sidekick waits, basically until he gets bored. Boredom should probablly be an internally clocked stat.
Once he''s done with that, the next thing I''d hope for is an a simple explanation of the current goal, and what the large goal is. "We''ve got to get out of here. XYZ is still waiting for us."
Then, the side kick would walk around the room, looking around at the objects of interest to point them out to the player. If it was hard to see at this point, he might do something like turn on a light (things that make the situation more visible, but don''t necessarrally solve the puzzle). Perhaps he''d even point them out in audio "Hey now, what''s this?" "Could this be important?"
If nothing happened still the sidekick start fidgeting around the door to the other room, having now gotten a signal from it that says "Goal is this way". Again, he''d toss out a few more indications that this is the right way to go. "I wonder what''s in here. We should check it out."
And that''s about as far as I''d take it. But let''s continue for the sake of excersize. Having gotten really bored, and fiddled with everything else in the room, he decides to go through the door. Having gone through that, he starts up a similar sequence as in the previous room. While looking around he finds the objects, and again, makes a hint at them being important, "Hey, stuff!"
If still, there is no response from the player, he might annoucne the names or uses, or even bring them into the other room. If the player still doesn''t do anything, The sidekick can fiddle around with the chair and broom a bit, possibly taking the "stand on me" and "poke with me" attributes in concert, and standing on the chair, unlatching the trap door with the broom, and escaping. The side kick then proceeds to clear out the mansion, making great riches, beating the game, and becoming a well renown hero.
That is, of course, why we neeed to be a little careful about them being able to handle everything, then they can play the game without us.
I''ll try to think up ways of coding this up later.
hope this helps out.
You are not too late to add to this.
I hear what you are saying, and I see things a little bit differently. I think the sidekick should act as intelligent as possible. For the player, this is interesting and realistic behavior to watch. And, there are other things the player can do simultaneously, such as solve another tangent problem, fight off something evil in the corners as the sidekick is working on an escape route, etc.
By just having the sidekick give hints at what to do, it seems as if the flavor of the experience has been changed to one deliberately designed around little puzzles in which the player is supposed to solve by receiving hints from his sidekick. In other words, the dynamicism of the experience has changed from one that contains self motivated agents doing their own things to one of agents leading the player on mini quests.
That does not preclude certain personality types which are more passive and make more suggestions to the player instead of being more proactive and brash though.
___________________________________
I hear what you are saying, and I see things a little bit differently. I think the sidekick should act as intelligent as possible. For the player, this is interesting and realistic behavior to watch. And, there are other things the player can do simultaneously, such as solve another tangent problem, fight off something evil in the corners as the sidekick is working on an escape route, etc.
By just having the sidekick give hints at what to do, it seems as if the flavor of the experience has been changed to one deliberately designed around little puzzles in which the player is supposed to solve by receiving hints from his sidekick. In other words, the dynamicism of the experience has changed from one that contains self motivated agents doing their own things to one of agents leading the player on mini quests.
That does not preclude certain personality types which are more passive and make more suggestions to the player instead of being more proactive and brash though.
___________________________________
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
Finished. A prototype's prototype. It's dirty, it's cheap, it cuts corners, but it works.
I use hard code to store all the game information, I reuse the same small structure for actions, goals and solutions, I have a linear goal system (do A, then do B, then do C), I don't even have a NPC structure at all (all the NPC info is stored in the objects..). The object AI functions use recursive calls to one another for messages.
But it works.
Now to study a bit for those stupid exams before they kick me out of the school for good.
P.S. I know my comments stink (and my lack of comments too), so I welcome all questions.
[EDIT]: link
Edited by - Diodor on January 25, 2002 3:38:03 PM
I use hard code to store all the game information, I reuse the same small structure for actions, goals and solutions, I have a linear goal system (do A, then do B, then do C), I don't even have a NPC structure at all (all the NPC info is stored in the objects..). The object AI functions use recursive calls to one another for messages.
But it works.
quote:
the log.txt output file
You see a door.
You see a diamond inside a glass case.
You see a wooden box.
You see a glass case.
You hit the glass case. Nothing happens.
You open the wooden box.
You see a key inside the wooden box.
You take the key.
Using the key, you scratch the glass case.
You hit the glass case. The weakened glass shatters.
You take the diamond.
You unlock the door with the key.
You open the door.
You pass through the door. You are win.
quote:
Excerpt from the verbose.txt log file
-------- GOAL COMPLETED ----------
------ new turn start -------
Goal : action enter (target none, tool none) .
wooden box action enter (target none, tool none)
key action enter (target none, tool none)
door action enter (target none, tool none)
door action unlock (target door, tool none)
wooden box action unlock (target door, tool none)
key action unlock (target door, tool none)
solution found : action unlock (target door, tool key)
glass case action unlock (target door, tool none)
diamond action unlock (target door, tool none)
glass case action enter (target none, tool none)
diamond action enter (target none, tool none)
You unlock the door with the key.
------ new turn start -------
Goal : action enter (target none, tool none) .
wooden box action enter (target none, tool none)
key action enter (target none, tool none)
door action enter (target none, tool none)
door action open (target door, tool none)
solution found : action open (target door, tool none)
glass case action enter (target none, tool none)
diamond action enter (target none, tool none)
You open the door.
------ new turn start -------
Goal : action enter (target none, tool none) .
wooden box action enter (target none, tool none)
key action enter (target none, tool none)
door action enter (target none, tool none)
solution found : action enter (target door, tool none)
glass case action enter (target none, tool none)
diamond action enter (target none, tool none)
You pass through the door. You are win.
Now to study a bit for those stupid exams before they kick me out of the school for good.
P.S. I know my comments stink (and my lack of comments too), so I welcome all questions.
[EDIT]: link
Edited by - Diodor on January 25, 2002 3:38:03 PM
If you think about it, isnt your system almost the same as scripting, but putting the scripts in the objects instead of the npc? I guess I cant really comment because my understanding of AI isnt too good.
My solution too this problem would be a more of a planning one. You basically make up a whole lot of things the NPC can do, like manipulate objects, give commands too other NPCs and move. Each of these actions come with an attribute cost required to do them. Then you give the NPC a final state that it is aiming for. You also give the NPC attributes like intelligence and strength and such, personality things. The NPC then generates a series of plans that would result in the final state being achieved, it then calculates the Intelligence, Strength and whatever attributes we give it costs for each path and decides which is most suited for its personality type. This is sort of like pathfinding for NPC''s when moving, except goalfinding. Im not sure if it would be speed efficient though? maybe someone could give me an idea if this would work?
My solution too this problem would be a more of a planning one. You basically make up a whole lot of things the NPC can do, like manipulate objects, give commands too other NPCs and move. Each of these actions come with an attribute cost required to do them. Then you give the NPC a final state that it is aiming for. You also give the NPC attributes like intelligence and strength and such, personality things. The NPC then generates a series of plans that would result in the final state being achieved, it then calculates the Intelligence, Strength and whatever attributes we give it costs for each path and decides which is most suited for its personality type. This is sort of like pathfinding for NPC''s when moving, except goalfinding. Im not sure if it would be speed efficient though? maybe someone could give me an idea if this would work?
quote: Original post by KingMolson
If you think about it, isnt your system almost the same as scripting, but putting the scripts in the objects instead of the npc? I guess I cant really comment because my understanding of AI isnt too good.
It absolutely isn''t a scripting analogue, especially if you read my take on the evolving nature of it in the other thread called "Annotated objects which drive behavior".
quote: Original post by KingMolson
My solution too this problem would be a more of a planning one. You basically make up a whole lot of things the NPC can do, like manipulate objects, give commands too other NPCs and move. Each of these actions come with an attribute cost required to do them. Then you give the NPC a final state that it is aiming for. You also give the NPC attributes like intelligence and strength and such, personality things. The NPC then generates a series of plans that would result in the final state being achieved, it then calculates the Intelligence, Strength and whatever attributes we give it costs for each path and decides which is most suited for its personality type. This is sort of like pathfinding for NPC''s when moving, except goalfinding. Im not sure if it would be speed efficient though? maybe someone could give me an idea if this would work?
This is much closer to what my idea really is. We have objects which provide asociated actions. Each action changes the world state and requires certain world states (constraints) to exist before that action can be initiated.
The NPC matches his desired goals to the changes/goals field of each action which presents itself to him. Then, given an action to perform, the constraints of that action are verified against the current world state. For all constraints which aren''t met, these constraints then become subgoals to achieve the higher goal. These are built into a graph called an agenda. When a complete plan is built enabling the final goal to be acheived, the plan can be executed. At all times, the agenda is analyzed to provide effecient completion of multiple goals simultaneously. If the world state changes as a plan is being executed, planning takes over to modify the agenda for the world states which conflict with the existing plan.
The knowledge base is object-centric, meaning new objects can be added in an encapsulated form complete with their associated goals and contraints.
Please read the evolving nature of this in the other thread.
___________________________________
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
Diodor, that is pretty cool. Are you going to elaborate on your theory and implementation?
___________________________________
___________________________________
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
*looks up 'elaborate'* Ah, OK.
To make things short, all the program has is objects (thing_t) and actions (action_t).
An action object has action type, action target, action tool. Some actions don't need a tool, some do. The target and the tool are an index into the 'thing' vector of objects. -1 stands for no object.
An object has flags, a possible relation with another object (being inside it for instance), and two action handling function pointers (the functions actually define the objects behavior - this allows for scripted implementation of objects). The function initThings creates the list of objects.
The flags store object information (if it is locked, closed, visible, etc., player memory: if the player knows about the object, if he tried some actions on the object that failed -eg. the glass case cannot be broken by hitting it; but at first it will offer the player the 'hit' solution anyway and after the player hits it and fails stops offering this solution - the player knows hitting doesn't work - or more exactly, the glass case knows that the player knows)
The first function of an object executes an action performed or applied to the object. The naming convention for these functions is *Exec. These functions are a sort of a game engine actually. Also, these functions are filled with assert checks, so they will not allow any incorrect action to be passed to them.
The second function of an object gets a goal as a parameter and produces a solution to reach that goal. (Naming convention *Solve)
A goal is an action structure with missing target or tool parameter. In my game I use the goals Look, Take, Enter. The NPC must look at whatever he can look at, take whatever he can and enter wherever there is an entrance. (the function 'initNPC' creates a list of goals)
A solution is an action structure with the required target and tool parameters set correctly. A solution is an actual action that can be executed. The solution is passed to the execute action of the objects involved in the solution.
An object's Solve function gets a goal as a parameter. This function decides whether this goal is attainable. If it cannot be attained, it returns (asking a "Open" goal to a key etc.). If there are some constraints to the goal, it sends out to the Solve functions of the other objects different goals that would remove those constraints.
In example, I send the door the goal (ENTER, wherever, whatever). The door then realizes that it is locked, so it broadcasts the goal (UNLOCK, door, whatever). The key realizes that it can fit as tool to the (UNLOCK, door, whatever) goal, so it offers the solution (Unlock, door, key). Or the key realizes that it can fit as tool but the player didn't take the key, so it offers as a solution (Take, key). The verbose.txt file in the archive shows exactly the goals, recursive calls, solutions and actions.
After querying all the Solve functions for the current goal, the program executes one of the offered solutions. (function 'doit' does it). Everything is then repeated. When no solution is offered to a goal, the program goes to the next goal.
(If I think about it, the *Solve functions work not unlike the unifying process in Prolog)
Edited by - Diodor on January 26, 2002 3:01:24 AM
To make things short, all the program has is objects (thing_t) and actions (action_t).
An action object has action type, action target, action tool. Some actions don't need a tool, some do. The target and the tool are an index into the 'thing' vector of objects. -1 stands for no object.
An object has flags, a possible relation with another object (being inside it for instance), and two action handling function pointers (the functions actually define the objects behavior - this allows for scripted implementation of objects). The function initThings creates the list of objects.
The flags store object information (if it is locked, closed, visible, etc., player memory: if the player knows about the object, if he tried some actions on the object that failed -eg. the glass case cannot be broken by hitting it; but at first it will offer the player the 'hit' solution anyway and after the player hits it and fails stops offering this solution - the player knows hitting doesn't work - or more exactly, the glass case knows that the player knows)
The first function of an object executes an action performed or applied to the object. The naming convention for these functions is *Exec. These functions are a sort of a game engine actually. Also, these functions are filled with assert checks, so they will not allow any incorrect action to be passed to them.
The second function of an object gets a goal as a parameter and produces a solution to reach that goal. (Naming convention *Solve)
A goal is an action structure with missing target or tool parameter. In my game I use the goals Look, Take, Enter. The NPC must look at whatever he can look at, take whatever he can and enter wherever there is an entrance. (the function 'initNPC' creates a list of goals)
A solution is an action structure with the required target and tool parameters set correctly. A solution is an actual action that can be executed. The solution is passed to the execute action of the objects involved in the solution.
An object's Solve function gets a goal as a parameter. This function decides whether this goal is attainable. If it cannot be attained, it returns (asking a "Open" goal to a key etc.). If there are some constraints to the goal, it sends out to the Solve functions of the other objects different goals that would remove those constraints.
In example, I send the door the goal (ENTER, wherever, whatever). The door then realizes that it is locked, so it broadcasts the goal (UNLOCK, door, whatever). The key realizes that it can fit as tool to the (UNLOCK, door, whatever) goal, so it offers the solution (Unlock, door, key). Or the key realizes that it can fit as tool but the player didn't take the key, so it offers as a solution (Take, key). The verbose.txt file in the archive shows exactly the goals, recursive calls, solutions and actions.
After querying all the Solve functions for the current goal, the program executes one of the offered solutions. (function 'doit' does it). Everything is then repeated. When no solution is offered to a goal, the program goes to the next goal.
(If I think about it, the *Solve functions work not unlike the unifying process in Prolog)
Edited by - Diodor on January 26, 2002 3:01:24 AM
quote: Original post by Diodor
An object''s Solve function gets a goal as a parameter. This function decides whether this goal is attainable. If it cannot be attained, it returns (asking a "Open" goal to a key etc.). If there are some constraints to the goal, it sends out to the Solve functions of the other objects different goals that would remove those constraints.
In response to the above about goals and constraints, have you thought about the way I suggested in the other thread? Basically I am proposing to setup ACTION data like this:
Door
action: openDoor(AGENT,DOOR)
changes: open(DOOR)
constraints: closed(DOOR), NOT locked(DOOR), nextTo(AGENT, DOOR)
Door
action: unLock(AGENT, DOOR, KEY)
changes: NOT locked(DOOR)
constraints: nextTo(AGENT, DOOR), holding(AGENT, KEY), locked(DOOR)
Key
action: get(KEY)
changes: holding(KEY)
constraints: NextTo(AGENT, KEY), holding(AGENT, nothing)
Now, the items in the changes field are the world states that will be in effect after the action is executed. The items in the constraints field are the world states that must be in effect for the action to be executed. Both the changes field and the constraints field take the exact same type of item: world states.
Therefore, accomplishing a goal is the process of finding the action that provides the desired changed world state. Then the constraints are checked. If they are not met, the constraints become the new subgoals.
Another advantage is the non-opaque nature of the data, facilitating explanation about goals and constraints.
quote: Original post by Diodor
In example, I send the door the goal (ENTER, wherever, whatever). The door then realizes that it is locked, so it broadcasts the goal (UNLOCK, door, whatever). The key realizes that it can fit as tool to the (UNLOCK, door, whatever) goal, so it offers the solution (Unlock, door, key). Or the key realizes that it can fit as tool but the player didn''t take the key, so it offers as a solution (Take, key). The verbose.txt file in the archive shows exactly the goals, recursive calls, solutions and actions.
I see that is basically what you are doing, but in a quick and dirty way, which is good! You made it work. Are you ready to now build a unified and structured approach to the problem?
quote: Original post by Diodor
(If I think about it, the *Solve functions work not unlike the unifying process in Prolog)
That''s a given. A robust unification problem is necessary for term rewriting of clauses with variables. If the terms do not unify, then they in fact don''t match, meaning they do not provide a solution to the goal.
___________________________________
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement