Progress Report 1 (Innovative Resources)
From EQUIS Lab Wiki
Extending Resources in LIAV
Rob Fletcher and Banani Roy
February 17, 2005
Contents |
Objectives and Motivation
Objective
It is our goal to provide a rich new selection of resources for the player to harvest. Providing the player with a wider selection of resources will be benefitial to both the longevity and the complexity of the gameplay. From the player's point of view, new choices will be introduced regarding the order in which resources can be harvested. Presently, collecting different resources will does not yield any benefit to the player. However, this work will indirectly support future expansions to the game. Eventually, special buildings or abilities might be available to the player in exchange for particular harvested goods. More elusive resources can present challenges to the player, encouraging them to continue playing. A solid system of resources will be the foundation of a solid in-game economy which will result in a new dimension of gameplay (the managing of resources).
Our complementary goal is to provide an interface for game developers to create and add new resources. Adding a new resource should not require a recompilation, only a few minor adjustments and additional definitions to a handful of configuration files.
Motivation
Our motivation is to make the game more realistic through the addition of resources as well as increasing the villager population. In doing this we hope to make the game environment a more attractive area to explore and for players to appreciate. As more resources are added, the game will increase in depth and fun by giving the player more decisions.
User Interface
The user interface to the game will remain fairly similar to the original. However it will become slightly more dynamic, it will change based on the resources available in the game.
HUD
The only additions to the game's primary HUD (Head's up display) will be the additional resources listed in the village status box (in the top-left corner). Again, this is currently hardcoded (the positions for each resource information display have been defined down to the pixel in a configuration file) however, it should be possible to allow for this list to grow based on the current set of available resources.
Notification
The other change to the overlay will be with respect to the harvest trigger notifications ("Press B to Chop Down Trees"). The original notification was tucked away in the skybox texture definitions. It is also stored as a large bitmap (not created with fonts at run-time like other HUD elements). This will have to be replaced with a dynamic notification which will reflect the type of resource. This will require each resource to have a verb associated with it as well as it's name. An example would be "Chop down" for "Trees", "Dig up" for "Rocks", or "Pump" for "Tube Well".
Art Requirements
Our current art requirements are minimal, since we are still generalizing the resource system. Once completed, we would be interested in any kinds of models which might be "harvestable". Depending on the game's design we could use flowers, cacti, boulders, turnips, or any variety of trees. It would also be nice to replace the OGRE head resource node with something more iconic to the resource. Animated resource nodes might be easier to spot in a field of static resources. A spinning coin with a picture of the resource stamped on it, for instance.
Project Milestones
Milestone 1: Rock
Our first milestone is to add a 'Rock' resource in such a way that it will not require recompilation and will be treated in the same way as the 'tree' resource.
Our first completed step has been to implement a new resource through the duplication of the existing tree code. As we went through the initial codebase with this in mind, we took note of what elements of the engine would require generalization in order to allow for a modular resource system.
The next step required in this milestone is to generalize the interface to allow for adding new resources without compilation. The biggest problem we will likely have with this will be dealing the AI rules, which currently are based on boolean variables tied directly to functions in the engine. There are approximately 200 lines of C++ code in the functions referred to by the AI rules. We have duplicated these for the rock resource to show that new resources can be added, but it becomes difficult should we want to add dozen new resources.
It is our hope to have completed Milestone 1 by the end of the course.
Milestone 2: Sheep
We also considered adding moving resources and resource nodes, to allow for resources such as sheep or cattle. If we are able to complete the first milestone, then we will look into allowing moving resources.
Milestone 3: Wells
In order to add a tubewell (which we initially proposed) we would need to have the general resource system implemented as well as a significant extension to the building system. It is not likely that we will even begin to implement this milestone.
Technology
The work we did can be seperated into the following steps:
- Updating the map to have rock areas
- Updating the overlay to reflect rock status
- Updating the source to allow rocks
- Modified the AI rules
- Modified villager population
Tools
mappainter.py
We used and modified the mappainter.py script to allow for the new resource. It currently accepts an XML file and a PNG image as input and processes them. The output is a collection of resource area maps, a heightmap, and a world texture map.
Currently the mappainter.py has a method (shebang) that will generate this output. It currently contains a list which has been hardcoded into its source that contains the list of potential resource areas (hill, tree, mountain, river, lake, and rock). This list does not allow for a general resource system. A slight change to the XML definition and the script will allow for a general approach. We have not made these changes yet, however they will be required:
Currently in mappainter.py:
# list of possible tag values in xml file taglist = [ "tree", "hill", "mtn", "river", "lake", "plain", "rock" ]
Reads:
<rock> <alpha color="8000ff" bleed="30" /> <hf scale="0.03" bottom="0.01" top="0.20" xo="2" yo="2" /> <tex> <perlin scale="0.02" xo="10" yo="10"> <color value="888888" /> <color value="bbbbbb" /> </perlin> </tex> </rock> <hill> <alpha color="07ab0b" bleed="30" /> <hf scale="0.03" bottom="0.01" top="0.20" xo="2" yo="2" /> <tex> <perlin scale="0.02" xo="10" yo="10"> <color value="(95,150,16)" /> <color value="(82,111,58)" /> </perlin> </tex> </hill>
A more general approach would be to use something like:
<resourcetype name="rock"> <alpha color="8000ff" bleed="30" /> <hf scale="0.03" bottom="0.01" top="0.20" xo="2" yo="2" /> <tex> <perlin scale="0.02" xo="10" yo="10"> <color value="888888" /> <color value="bbbbbb" /> </perlin> </tex> </resourcetype>
So by modifying the XML, the name of the resource can be found in an attribute and then processed without requiring any modification of the mappainter.py
Classes Modified
CAXWorldResourceManager
In the CAXWorldResourceManager class we made extensive changes through the duplication of the existing tree code. The CAXWorldResourceManager is responsible for initializing the game's resources. We added methods which will read the "rocks.png" image (generated by mappainter.py) and scatter rocks throughout the area defined by rocks. We are currently using the "cube" mesh for our rocks. This is another place where the generation has been hardcoded and will have to be generalized. The different resource types should be read from a file, and then the required map distribution images should be generated.
The following are the methods we added to this class to allow for rocks:
void plantRocks ( string alphamap ); void insertRockResourceNodes( string alphamap); CAXBuilding* getClosestRock( const Vector3& pos ); CAXBuilding* getClosestStoneResourceNode ( const Vector3& pos ); CAXBuilding* getRockForStoneResourceNode ( CAXBuilding& rsrcnode ); void removeRock ( CAXBuilding* rock); unsigned numCollectedStone(); void addCollectedStone ( unsigned units ); void removeCollectedStone ( unsigned units );
Ideally these methods (which are mirrored from the tree methods) should be generalized to look something like:
void plantResources ( string alphamap, ResourceType*, int density ); void insertResourceNodes( string alphamap, ResourceType*, int density); CAXBuilding* getClosestResource( const Vector3& pos, ResourceType* ); CAXBuilding* getClosestResourceNode ( const Vector3& pos, ResourceType* ); CAXBuilding* getResourceForResourceNode ( CAXBuilding& rsrcnode ); void removeResource ( CAXBuilding* rock); unsigned numCollected( ResourceType* ); void addCollected ( unsigned units, ResourceType* ); void removeCollected ( unsigned units, ResourceType* );
CAXGameState
We have modified the code in ActionState to check which resource node is the closest to the avatar to determine what action to take. Our next goal will be to allow the ActionState to determine what action to take based on the contents of the resource script files. This will require internal changes to ActionState and ProximityTrigger classes, but we do not suspect any architectural reorganizations will be needed.
We modified the method which is called when we enter the CAXActionState (that is, when a trigger has been activated) to grab a resource node of each type, as opposed to just a single one which was its original behaviour. If this state had been passed a ResourceType, then it would know what kind of resource to look for, and we would not have to do the proximity test. This will be removed when we generalize the resources.
void CAXActionInGameState::stateEntered( CAXGameState* oldState )
We also added a method that does a distance check between two points:
float CAXActionInGameState::getDistance(float x1, float y1, float x2, float y2)
We use this to determine which node is closest to the avatar. (See figure 1)
Bug fixed
At one point the 'int CAXActionInGameState::frameStarted( float elapsed )' function of the GameState would occasionally crash. This was caused by a possible situation where it would finished but not return a value. This was correcting by tracing through the if statement and finding the offending path.
Modifying the AI Routines
We had to add appropriate villager intelligence to allow them to manage the new job "CHOP_ROCK". We copied all of the existing rules for wood collecting and harvesting and duplicated them, changing the rules to be applicable to the rock resource.
Here are the new rules we have added (also showing the functions we have duplicated):
IF job_chop_rock AND at_rock AND NOT chop_rock AND NOT going_to_stone_drop THEN start_timer AND chop_rock AND NOT move IF job_chop_rock AND chop_rock AND chop_rock_timeout THEN remove_rock AND NOT chop_rock AND drop_off_stone AND going_to_stone_drop AND move IF job_chop_rock AND going_to_stone_drop AND at_stone_drop_off THEN drop_stone AND NOT going_to_stone_drop AND go_to_rock AND move IF job_chop_rock AND NOT chop_rock AND NOT going_to_stone_drop AND NOT move THEN go_to_rock AND move
A problem with the current implementation of the AI is that it doesn't take into account the villagers ever stopping. Eventually the villagers will run out of resources, which could cause the software to hang. There should be a mechanism for the villagers to take a break, or to quit entirely. We have started to pencil-in a 'quit_job' rule which we haven't completely implemented yet. It should allow for a higher turnover in villager job assignment.
The new rule we are working on:
# IF job_chop_rock AND going_to_stone_drop AND at_stone_drop_off THEN drop_stone AND NOT going_to_stone_drop AND quit_job AND NOT move
Although the duplication of the rules did work and allow for villagers to harvest a new resource, the amount of code we had to copy (200 lines) makes it prohibitive to continue to duplicate the code for other resources. A solution to this would be to pass parameters along with the rules, such as "is_harvesting" instead of "is_chopping", however this might break the way that the rule script interacts with the program.
Interaction with other groups
We have not needed to interact with any other groups, yet. Our problem area does not depend on any areas being modified by the other teams. We may need some models from the art group before the project is finalized.
However, as we will begin work on the AI routines, we will have to remain in contact with Edwin and Sean who are also concerned with the behaviour of villagers.