CS-107: Mini-project 2
Cooperative games
Version 1.2
Contents
1 Introduction 4
2 ICoop de base ICoop Basics (Step 1) 7
2.1 Preparing the ICoop ICoop . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Adaptation of ICoopBehavior . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.1 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Draft of Main Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3.1 Drawing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.2 Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.3 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 Doors: Contact Interactions . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4.1 Doors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.2 ICoopPlayer as an Interactor . . . . . . . . . . . . . . . . . . . . . 12
2.4.3 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5 Second Character . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5.1 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6 Explosives: Remote Interactions . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6.1 Obstacles and Explosives . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6.2 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7 Reset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7.1 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.8 Validation of Step 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3 First Cooperation (Step 2) 17
13.1 Health Bar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2 Dialogues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.1 Activating a Dialogue . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2.2 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3 Collectible Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.1 Collecting Explosives . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.2 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.3 The ElementalItem . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.4 The Orbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3.5 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.4 Elemental Walls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.4.1 Elemental Category Role . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4.2 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.5 Assistance, Healing, and Invulnerability Periods . . . . . . . . . . . . . . . . 24
3.5.1 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.6 Maze Area . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.6.1 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.7 Validation of Step 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4 Enemies and Battles (Step 3) 26
4.1 Equipment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.1.1 Inventory Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.1.2 Inventories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1.3 Collection and Inventory . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.1.4 Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.1.5 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.2 Enemies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.2.1 Projectiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.2 Fire-throwing Skulls . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.3 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.2.4 Bomber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.2.5 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.3 Battles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.3.1 Staffs and Water/Fire Orbs . . . . . . . . . . . . . . . . . . . . . . . 35
24.3.2 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.3.3 Weapon Usage by Characters . . . . . . . . . . . . . . . . . . . . . . 35
4.3.4 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.4 Validation of Step 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5 Final challenge (Step 4) 38
5.1 Keys and Teleporters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2 Area Arena . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.3 Gateway to the manor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.4 Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.5 Validation of Step 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6 Extensions (Step 5) 42
6.1 New actors or character extensions . . . . . . . . . . . . . . . . . . . . . . . 42
6.2 Pause, game ending and ‘reset’(~2 to 5pts) . . . . . . . . . . . . . . . . . . 43
6.3 Validation of Step 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.4 Contest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
7 Appendices 45
7.1 KeyBindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
7.2 Basic Conffguration of the Maze Area . . . . . . . . . . . . . . . . . . . . . . 45
7.3 Creating Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.3.1 ICoopPlayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.3.2 Explosive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.3.3 Orbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.3.4 Hearts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.3.5 Staves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.3.6 Flames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.3.7 Water or Fire Projectiles . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.3.8 Death of foes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.3.9 Fire-Shooting Skulls . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.3.10 Artiffcers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
31 Introduction
This document uses colors and contains clickable links. It is best
viewed in digital format.
Over the past weeks, you have become familiar with the fundamentals of a small ad-hoc
game engine (see tutorial) that allows you to create tile-based games in two dimensions,
similar to RPGs.
The goal of this mini-project is to leverage this knowledge to create one or more small
concrete implementations of a cooperative variant named ICcoOp, a type of game involving
two main characters. These characters will collaborate to achieve objectives or defeat
enemies. The base game you are tasked with creating is inspired by games like Fireboy and
Watergirl. Figure 1 shows some fragments of the basic draft that you can then enrich as you
wish, according to your imagination and creativity. Section 6 also includes a short video
example of a potential game you could create.
Beyond its entertaining aspect, this mini-project will naturally allow you to practice the
fundamental concepts of object-oriented programming. It will let you experience how a
design situated at an appropriate level of abstraction enables the creation of programs that
are easily extensible and adaptable to different contexts.
You will progressively increase the complexity of the desired features and the interactions
between components, step by step.
The project consists of four mandatory steps and one optional step:
• Step 1 (”Basic Game”): By the end of this step, you will have created, using the tools
of the provided game engine, a basic instance of ICCoOp where two main characters
can follow each other from one area to another and interact with objects.
• Step 2 (”First Cooperation”): During this step, the two characters will begin to assist
each other in facing hostile walls. Rudimentary dialogues will be introduced, along
with tracking the characters’ health points.
• Step 3 (”Belligerent Adversaries”): This step will enable your characters to face
enemies and engage in battles with them using equipment.
• Step 4 (”Final Challenges”): This step will introduce new areas with new challenges
to tackle cooperatively, utilizing the concept of logical signals.
• Step 5 (Optional Extensions): During this step, various more open-ended extensions
will be proposed, allowing you to enhance the game created in the previous step or to
create new games of your own design.
Code a few extensions (of your choice) to earn bonus points and/or
participate to the contest.
4Figure 1: Some scenes from the game where two players help each other from area to area,
collecting items and battling various hostile (or not) creatures.
Here are the main instructions/guidelines to follow for coding the project:
1. You will not modify the code of game-engine. Beware of
IntelliJ’s «quick ffx» proposal.
2. The project must be coded using standard Java tools (imports
starting with java. or javax.). If you have doubts about
using a particular library, ask us, and be especially cautious
with the alternatives IntelliJ might suggest importing on your
machine. The project speciffcally uses the Color class. You
must use the java.awt.Color implementation and not other
implementations from various alternative packages.
3. Your methods must be documented according to Javadoc standards
(refer to the the class TextGraphics of game-engine for
inspiration).
4. Your code must adhere to standard naming conventions and
be properly modularized and encapsulated. In particular,
avoid intrusive public getters on modiffable objects.
(continued on the next page . . .)
54. The instructions provided may sometimes be very detailed.
This does not mean they are exhaustive. The methods
and attributes needed to achieve the desired functionalities are
not all described, and it will be up to you to introduce them as
you see fit, ensuring proper encapsulation.
5. Your project must not be stored on a public repository
(e.g., GitHub). For those familiar with Git, we recommend
using GitLab: https://gitlab.epfl.ch/, but any repository type
is acceptable as long as it is private.
The first step is deliberately guided. Its primary goal is to deepen your understanding of
the provided framework and to begin leveraging it in a concrete manner.
62 ICoop de base ICoop Basics (Step 1)
The goal of this step is to start creating your own game of the ICoop type.
This basic version will include two main characters that can be controlled individually and
are capable of interacting with objects. The latter point will be implemented using the more
general mechanism of interactions between actors, as described in tutorial 3.
This game will involve:
• Two main characters;
• Doors that the characters can pass through. This will involve a contact interaction: a
character must be in a cell containing an ”actor” labeled as a ”door” to pass through
it;
• An explosive that the characters can activate (via remote interaction);
• a rock that the explosive, once activated, can destroy (again via remote interaction).
The two characters will follow each other during area transitions.
You will work in the provided icoop package.
2.1 Preparing the ICoop ICoop
The solution for the tutorial is provided in the tutos folder, and you
can use it as inspiration to get started.
Prepare an ICoop game inspired by Tuto2. This game will initially consist of :
• The ICoopPlayer class, which models a main character, should be placed in icoop.actor;
leave this class empty for now, we will come back to it a bit later;
• the ICoop class, equivalent to Tuto2, to be placed in the icoop package; don’t forget
to adapt the getTitle() method, which will return a name of your choice (e.g.,
"ICoop");
• une classe ICoopArea équivalente à Tuto2Area, à placer dans un sous-paquetage
icoop.area;
• A class ICoopArea equivalent to Tuto2Area, to be placed in a sub-package icoop.area;
• Classes Spawn and OrbWay inheriting from ICoopArea, to be placed in the package
icoop.area (they are equivalent to the classes Ferme or Village in the tutorial). For
the title, use "Spawn" and "OrbWay" to ensure proper alignment with the graphical
resources;
• A class ICoopBehavior analogous to Tuto2Behavior to be placed in icoop, which
will contain a public class ICoopCell equivalent to Tuto2Cell.
You will ensure that the initial positions of the main characters to come are dictated by a
method returning specific values for each area: for example, (13,6) for the red character
and (14,6) for the blue one in the area Spawn, and (1,12) and (1,5) for the area OrbWay.
For the type of games we’re interested in, it is best to centre the view as closely as possible
in the window. To do this, simply add the following redefinition to ICoopArea :
7@Override
public boolean isViewCentered() { return true; }
2.2 Adaptation of ICoopBehavior
First, a few updates need to be made to the classes ICoopBehavior and ICoopCell. The
enumerated type describing the cell types will have an additional field indicating whether
the cell can be flown over or not (a canWalk field, similar to isWalkable, and a canFly field
indicating if one can fly over the cell and which can be used later in the project) :
NULL(0, false , false),
WALL(-16777216, false , false),
IMPASSABLE (-8750470, false , true),
INTERACT(-256, true , true),
DOOR(-195580, true , true),
WALKABLE(-1, true , true),
ROCK(-16777204, true , true),
OBSTACLE (-16723187, true , true)
Additionally, the nature of the cells will not be the only factor determining whether a cell
allows an actor to enter (via its method canEnter). The actors already present in the cell
will also play a role. In the case of the ICoop game, a cell can have at most one non-passable
actor within its set of entities (inherited from Cell): two non-passable actors cannot coexist
in the same cell !
The takeCellSpace() method indicates whether an actor is passable (allows others to ”step
on it”) or not. It is passable if its takeCellSpace() method returns false.
Make the suggested adaptations above in the ICoopBehavior class.
2.2.1 Task
You are required to implement the concepts described above according to the given specifications
and constraints. Run your ICoop game. Verify that the empty area in Figure 2 is
displayed.
2.3 Draft of Main Characters
For now, code ICoopPlayer in the same spirit as GhostPlayer.
One important difference is that the characters, like many upcoming actors, will ”serve” a
natural element (typically the ”water” or ”fire” element).
You should therefore introduce a category called ElementalEntity. Any actor behaving
as an ElementalEntity must define a method element() that returns the natural element
the actor serves. This will, of course, apply to the main characters. The element served by
the character will be specified during construction.
8Figure 2: Welcome area
2.3.1 Drawing
Another significant difference from GhostPlayer is that the image used to draw an ICoopPlayer
will depend on its orientation and will be animated.
To draw the character, you will use an object of type OrientedAnimation. There will be
several possible animations as the project progresses, but for now, only the basic animation
needs to be implemented. This can be constructed as described in Appendix 7.3.11
. Since we
will create two characters, the name of the image used to create the animation will naturally
differ for each. You should consider this name as configurable during the character’s
construction.
2.3.2 Behavior
An ICoopPlayer is a non-passable actor that is oriented downward by default. It behaves
(updates itself) like a MoveableAreaEntity but with additional features:
• It must be movable using keys, similar to the GhostPlayer coded in the tutorial;
• Its current animation must also be updated based on the following algorithm: if a
movement is in progress, the current animation must undergo an update; otherwise,
it must undergo a reset.
One of the unique aspects of ICoop games is that two characters need to be controlled,
requiring two different sets of keys. The specific set of keys for a given character can be configured
during construction as a KeyBinding.PlayerKeyBindings. Refer to Appendix 7.1 to
understand this data type. Adapt your movement method so that the ”directional arrow”
keys used in the tutorial are replaced by keys.up(), keys.down(), keys.right(), and
keys.left(), where keys is the set of keys associated with the character.
Once this draft of ICoopPlayer is complete, you can start adapting the begin method of
ICoop and testing your game. Initially, you will introduce only one of the two characters.
At launch, a player of type ICoopPlayer will be created at its designated position in
1Refer to the documentation for OrientedAnimation if you want to understand the role of the parameters
in creating the animation.
9Figure 3: Door from “Spawn” area to “OrbWay” area
the current area. The name of the images associated with its basic animation will be
"icoop/player", and the keys used to control it will be those specified by
KeyBindings.RED_PLAYER_KEY_BINDINGS.
2.3.3 Task
You are required to code the concepts described above according to the given specifications
and constraints.
Run your ICoop game. Verify that it behaves as shown in the following short video:
https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/startStep1.mp4. Specifically,
ensure that:
1. The game starts by displaying an ICoopPlayer facing forward;
2. The character can move freely across the area using the keys specified by
KeyBindings.RED_PLAYER_KEY_BINDINGS (W, A, S, and D if unchanged) but cannot
leave the area;
3. Its graphical representation adapts correctly to its orientation;
4. Its movements are animated.
2.4 Doors: Contact Interactions
You are now tasked with applying the interaction framework suggested in the third part of
the tutorial (Clickable Link)2
.
The first interaction we will focus on is a contact interaction with a ”door” actor that will
allow an ICoopPlayer to transition from one area to another.
2A complementary video is also available to explain the implementation of interactions: mp2-
interactions.mp4
102.4.1 Doors
As explained in the tutorial, if an entity undergoes interactions, it must be modeled as an
Interactable; if it initiates interactions, it must be modeled as an Interactor (and it can
be both).
As a first category of Interactable, we already have ICoopCell (for example, if a character
is in contact with certain types of cells, it could adopt a different behavior, such as sliding).
We will not exploit this feature for now but will instead introduce a new Interactable,
”door,” which will have a more immediate utility.
A door, Door, to be coded in the icoop.actor package, is an actor of type AreaEntity
that allows transitions to a destination area.
This actor is characterized by:
• the name of the area to which it allows a transition (a string);
• and the set of arrival coordinates (DiscreteCoordinates) in the destination area.
This set will be variable in size, though for the purposes of this project, it will typically
have a size of two : arrival coordinates for the red character followed by those for the
blue one.
The tutorial introduced the notion of a signal, which can be used here to make the game
more engaging by incorporating puzzle-solving elements. For example, to obtain a weapon
for defense, the character might need to find a key that opens a door to a room containing
the desired weapon. The provided tools could typically catalog a key actor as a signal
attached to this door: once the signal is activated (the key is picked up), the door opens.
A ”door” actor will therefore also be characterized by a signal (of type Logic) that models
the conditions under which it is open or closed.
The constructor for a Door takes the following parameters: the name of the destination area,
the signal conditioning its opening, the possible arrival coordinates in the destination area,
the area it belongs to, the position of its main cell, and optionally the list of coordinates
of the cells it occupies in addition to its main cell (expressed using an ellipsis in a second
constructor).
The method getCurrentCells() must therefore be overridden in Door to return the set of
these occupied coordinates.
A Door is a passable actor that accepts contact interactions and whose drawing method
does nothing by default. Indeed, doors will coincide with elements of the scenery and will
not be drawn as distinct entities. As a result, their orientation is not very important.
You are tasked with introducing this new actor and registering an instance of it with the
following characteristics in the Spawn area:
destination arrival coord. signal main cell other coord.
”OrbWay” (1,12)(1,5) Logic.TRUE (19,15) (19,16)
This amounts to placing a door at the location indicated in Figure 3.
Also, register the doors that allow the transition in the other direction, from OrbWay to
Spawn:
11destination arrival coord. Signal main cell other coord.
”Spawn” (18,16),(18,15) Logic.TRUE (0,14) (0,13),(0,12),(0,11), (0,10)
”Spawn” (18,16),(18,15) Logic.TRUE (0,8) (0,7), (0,6), (0,5), (0,4)
The value Logic.TRUE indicates that these doors are open without any condition. The
locations of the doors can be determined by counting the coordinates of the red tiles in the
”behavior” images (for example, see resources/images/behaviors/Spawn.png).
2.4.2 ICoopPlayer as an Interactor
Now that we have a concrete entity capable of undergoing interactions, let’s focus on
ICoopPlayer as an entity initiating interactions. Start by ensuring that all ICoopPlayer
instances become Interactor entities (they can initiate interactions with Interactable
entities).
As an Interactor, ICoopPlayer must define the following methods:
• getCurrentCells(): its current cells, which are reduced to the set containing only
its main cell (as you have already implemented);
• getFieldOfViewCells(): the cells forming its field of view, consisting of the single
cell it is facing:
Collections.singletonList
(getCurrentMainCellCoordinates().jump(getOrientation().toVector()));
As an Interactor, it will always request all contact interactions (wantsCellInteraction
always returns true). Whether it requests remote interactions (wantsViewInteraction)
will be determined by the user: pressing its keyBindings.useItem() key will indicate that
the character wants a remote interaction. For example, if a character is facing an explosive
and the user wants to activate it, they press the key corresponding to keyBindings.useItem()
(by default, E for the red player and O for the blue player).
We can now handle the possible interactions at this stage: in the subpackage icoop.handler,
complete the ICoopInteractionVisitor interface, which inherits from AreaInteractionVisitor.
This interface must provide default definitions for the interactWith methods for all Interactor
entities in the ICoop game with:
• a game cell (ICoopCell);
• a main character in the game (ICoopPlayer);
• a door (Door).
These (default) definitions will have an empty body to indicate that, by default, the interaction
does nothing (ICoopPlayer, as an Interactor in the ICoop game, must provide a
more specific definition of these methods if necessary).
Every concrete Interactable must now indicate that it accepts having its interactions
managed by an interaction handler of type ICoopInteractionVisitor.
void acceptInteraction(AreaInteractionVisitor v, boolean
isCellInteraction) {
((ICoopInteractionVisitor) v).interactWith(this ,
isCellInteraction);
}
12This method may contain more code depending on the case, but for now, it is sufficient for
the three types of Interactable available: ICoopPlayer, ICoopCell, and Door.
To allow ICoopPlayer to manage interactions of interest more specifically, define a private
nested class ICoopPlayerInteractionHandler inside the ICoopPlayer class, implementing
ICoopInteractionVisitor. Add the necessary definitions to handle interactions with doors
more precisely, such that during a contact interaction with a door, if the door’s signal is
activated (method isOn()), the character:
1. leaves its current area (method leaveArea());
2. arrives in the destination area at the first possible arrival coordinate for that area
(method setCurrentArea).
Note: Only the game knows all the areas and is capable of transitioning the character
from one area to another using the leaveArea and setCurrentArea methods. Therefore,
this process cannot be directly coded in the character’s interaction method with the door.
Instead, the method should only inform the character that it is traversing a door (and specify
which one), and the character must provide the necessary information to the game!
2.4.3 Task
You are required to implement the concepts and processes described above according to the
given specifications and constraints. Run your ICoop game. Verify that the main character
can transition from the "Spawn" room to the "OrbWay" room (and vice versa) through the
location corresponding to the door in Figure 3. The lower door in the OrbWay room will
only be reachable once the second character is introduced.
2.5 Second Character
We now introduce what makes ICoop games unique: the presence of a second character.
The introduction of this character raises the question of how to properly center the camera,
which until now has followed the single character as in the tutorial.
The adopted idea is to center the camera on a point equidistant from the two characters.
To simplify the required calculations, a CenterOfMass actor is provided.
You are now tasked with evolving the ICoop game to:
• include a second character as a core feature;
• ensure that the begin method creates this character and centers the camera on a
CenterOfMass object constructed using the two characters.
The second character will:
• start at the position dictated for them in the current area;
• use the image "icoop/player2" as their associated sprite;
• be controlled with the keys specified by KeyBindings.BLUE_PLAYER_KEY_BINDINGS.
If this character transitions via a door, it must arrive at the second arrival coordinate in
the destination area. Additionally, ensure that the two characters follow each other: if one
13character passes through a door, the other follows, arriving at the destination coordinate
assigned to them by the door. Both characters will always occupy the same area!
Finally, to ensure that the initially chosen scale factor does not hinder the joint observation
of both characters, the update method of the game dynamically recalculates it (and reassigns
it using setCameraScaleFactor) with a heuristic formula such as:
max(DEFAULT_SCALE_FACTOR, DEFAULT_SCALE_FACTOR * 0.75
+ distance(position_playerA, position_playerB) / 2)
You are free to refine this formula as you see fit. The distance can be calculated as the length
of the vector position_playerA - position_playerB (methods sub and getLength of
Vector).
2.5.1 Task
Once the suggested modifications have been implemented, run your ICoop game. Verify
that :
• a second character appears at the start of the game;
• it can be controlled using its specific key bindings;
• it behaves like the first character but is drawn with a blue-tinted animation;
• each character can transition from "Spawn" to "OrbWay" and vice versa. In this case,
the other character follows, and both characters arrive in the area at the positions
designated for them by the crossed door. An example of a door passage is provided
in the following video:
https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/secondPlayer.mp4.
2.6 Explosives: Remote Interactions
To solidify the interaction management mechanism, the final part of this step involves
introducing two new actors: explosives and obstacles, with the idea that an explosive can
destroy certain obstacles remotely.
2.6.1 Obstacles and Explosives
An obstacle is an actor derived from AreaEntity and drawable by default using the sprite
"rock.2". By default, it is non-passable and it unconditionally accepts any type of interaction.
Rocks are a specific type of obstacle, drawable using the sprite "rock.1", and they
can be destroyed: they are non-passable and are only drawn if they are not destroyed.
An explosive is a passable actor inheriting from AreaEntity and playing the role of an
Interactor. It can be activated and is associated with a timer (an integer) initialized at
its creation. It is created in a deactivated state. Once activated, on each of its update, its
timer is decremented (e.g., by one unit). When the timer reaches zero, the explosion occurs
(the explosive enters the explosion state), which triggers the display of a specific animation,
after which it disappears from the game.
14An explosive, as an Interactor, requests remote or contact interactions when it is in the
explosion state. For now, it interacts remotely only with rocks. The effect of the interaction
with rocks is, of course, to destroy them.
The explosive’s field of action, getFieldOfViewCells(), consists of all 4 cells immediately
adjacent vertically and horizontally to its main cell. getCurrentCells() should be coded
in the same way as for ICoopPlayer.
As an Interactable, an explosive can undergo remote interactions if it has not exploded,
and contact interactions only if it is neither activated nor exploded.
Graphically, the explosive can be represented with an animation. It must disappear after
its explosion, which should be accompanied by another specific animation. Refer to
Appendix 7.3.2 for more precise details on this.
Finally, ensure that when a character interacts remotely with an explosive, it becomes
activated. Remember that characters express the desire for a remote interaction using their
useItem() key (default: 'E' and 'O' respectively).
To complete this step, create an explosive and a rock at respective positions (11,10) and
(10,10) in Spawn.
2.6.2 Task
You are required to implement the concepts described above according to the given specifications
and constraints.
Run your ICoop game. Verify that it behaves as shown in the following short video:
https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step1Explosion.mp4; specifically:
•
that characters cannot pass through the rock but can pass through the explosive;
• that each character can activate the explosive via remote interaction but only remotely;
• that once the explosive is activated, the timer starts and the explosion occurs after a
short delay;
• that the explosion is accompanied by an animation and that there is no trace of the
explosive once the animation ends;
• that the explosion destroys the rock, and its location, now empty, becomes passable
for the characters.
2.7 Reset
To facilitate testing, you will equip ICoop with the following controls: the key KeyBindings.RESET_GAME
(’R’ by default) should perform a game reset, and KeyBindings.RESET_AREA (’T’ by default)
should perform an area reset.
In the first case, the game should restart in the same conditions as when it was first
launched. In the second case, only the current area is restarted. In both situations, the
begin methods should prove useful. For restarting the current area, you will need to find
a way to recreate/replace the characters at their ”spawn” positions. Note that the begin
methods for a game or an area (as provided by the template) clear the relevant collections
15(areas, actors, etc.); thus, there is no need to handle this explicitly.
2.7.1 Task
Verify that both reset keys function as expected and that the characters reappear at the
”spawn” positions specified by the areas. Test both keys in both existing areas.
2.8 Validation of Step 1
To validate this step, all the checks from sections 2.2.1, 2.3.3, 2.4.3, 2.5.1, 2.6.2, and 2.7.1
must have been completed.
The ICoop game, whose behavior satisfies the validation steps above, must be
submitted at the end of the project.
Question 1
The framework established, as explained in the tutorials and practically applied in
this first part of the project, might initially seem unnecessarily complex. Its advantage
is that it models, in a very general and abstract way, the inherent needs of many
games where actors move on a grid and interact either with each other or with the
grid’s contents. How could you leverage this to implement a Pacman-like game? What
would need to be defined?
In the rest of the project, you will need to code many more interactions
between actors or with the cells. All upcoming interactions must be
implemented according to the framework established in this part and
must not rely on type tests on objects.
163 First Cooperation (Step 2)
The objective of this second part of the project is to introduce hostile actors that will harm
the main characters by inflicting damage. Means of protection against these dangers will
also be introduced to help the characters survive their now perilous adventures.
Specifically, this step aims to enhance the game by introducing the following new elements:
• Characters will become vulnerable to certain types of damage that cause them to lose
health points; you’ll anticipate water, fire or physical damages , and these damages
won’t just apply to characters;
• Fire walls will inflict fire damage on characters;
• Water walls will inflict water damage;
• Orbs (fire or water type) will grant characters immunity to these types of damage;
• Pressure plates will disable walls: the blue character can stand on a pressure plate
to disable a fire wall (and vice versa for water walls and the red character), enabling
both characters to start cooperating to overcome these hostile walls.
The implementation of these new elements will again rely on the concept of signals already
used for doors in the previous step.
During this step, visual elements will be enhanced to display the characters’ health status
via a health bar. Simple dialogues will also provide useful hints during gameplay.
Below, you will find the specifications to follow, along with some guidelines.
3.1 Health Bar
Figure 4: Health bars for the characters
Each ICoopPlayer will now be equipped with a health bar displaying their ”health status”
(see Figure 4). A Health class is provided in the actor directory to simplify the implementation
of this feature.
You will consider that an ICoopPlayer has a maximum number of health points (an integer
identical for all characters, e.g., 5). Each ICoopPlayer will be associated with a health bar
initialized as follows:
new Health(this , Transform.I.translated(0, 1.75f), MAX_LIFE ,
true);
The first parameter attaches the bar to the current actor (as health points were in the
tutorial), the second indicates how much to offset the bar from the actor in its relative
frame and the third one is a integer corresponding to the greatest number of points. The
17last parameter allows coloring the health bar green or red depending on whether the actor
is friendly or hostile (default is friendly).
The health bar must be drawn along with the character.
Study the Health class functionality to determine how to increase or decrease the health
points it represents.
Then, model the following for an ICoopPlayer:
• They can become immune (temporarily or not) to a certain type of damage (one type
at a time; initially, they are vulnerable to everything);
• They can lose a given number of health points due to a specific type of damage (e.g.,
an explosive inflicts physical damage causing a fixed loss of health points);
• Once they have lost health points (regardless of the cause), they benefit from a short
immunity period, the duration of which is identical for all characters.
The method that handles health loss due to a specific type of damage can be implemented
using the following algorithm: if the character is immune to the given damage type or is
in their immunity period, nothing happens. Otherwise, their health bar should reflect the
desired loss of health points.
Complete the Explosive class so that its interaction with an ICoopPlayer inflicts physical
damage, causing a fixed loss of health points. This value will be identical for all explosives
(e.g., 2). The duration of the immunity period will also be the same for all characters for
simplicity.
Guideline: You can implement the immunity period by drawing inspiration from the timer
mechanism of the explosive. A value such as 24 can be used for the immunity duration.
Character Drawing Adjustment: To visualize immunity status, modify the drawing of
ICoopPlayer so that, when immune, their animation is only drawn every other frame (e.g.,
only when the immunity timer is even).
Reset Adjustment: Reset keys must now ensure that characters restart with a full health
bar for both game resets and current area resets. For simplicity, consider that they also
restart with no immunity period.
End of Life: Adapt your ICoop game so that the loss of all health points by either
character triggers a reset of the current area.
3.1.1 Task
You are required to implement the concepts described above in accordance with the specifications
and constraints.
Run your ICoop game. Verify that it behaves as shown in the following short video:
https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step2-1.mp4; specifically:
18Figure 5: Help dialogues.
1. A health bar appears near each character, is fully green at the start, and follows the
associated character as they move;
2. The explosive, once detonated, causes health point loss for the character in its proximity
(e.g., if the character that activated the explosive did not move far enough away);
3. The character’s sprite flashes when they take damage, indicating they have entered
an immunity period.
Also, verify that the reset function restores health points and that the ”death” of either
character triggers a reset of the current area. The immunity to specific types of damage will
be tested later.
3.2 Dialogues
Many keys are already involved in interactions, and it is useful to document them at the
start of the game. Additionally, games of this type can quickly become unplayable without
well-timed hints. The idea here is to introduce a ”dialogue” component to meet these needs,
as shown in the example in Figure 5.
The template already provides a Dialog class (in the engine.actor package) that can be
used for this purpose. To create a dialogue, simply associate it with text stored in the
resources (subfolder dialogs), for example:
new Dialog("orb_water_msg");
The update method of Dialog allows the text to be scrolled. The isCompleted method
returns true when all the text has been displayed.
Dialogues will be used here to provide hints from the game to the player. This can be
implemented by equipping ICoop with:
• An attribute of type Dialog to model the hint to be displayed at a given time (the
19”active dialogue”). A dialogue is considered active if this attribute is not null;
• A setActiveDialog method to assign a value to this attribute.
Next, modify the ICoop class so that:
• The current area does not update when a dialogue is active in the game (the area will
only be drawn). The active dialogue must, of course, also be drawn;
• The active dialogue (if any) is updated when the NEXT_DIALOG key is pressed:
kbd.get(KeyBindings.NEXT_DIALOG).isPressed()
where kbd is the Keyboard object associated with the game;
• The game resumes normal behavior when an active dialogue is completed (isCompleted()
method of Dialog).
3.2.1 Activating a Dialogue
The question then arises of how to assign a value to the game’s ”active dialogue” attribute,
given that it is generally a game component, not the game itself, that knows when this
should happen.
For example, if you want a hint to be displayed when a character interacts with an actor,
the method managing this interaction should call the method that instantiates the game’s
active dialogue. This method (or the class to which it belongs) must then be aware of the
game. Similarly, if a hint needs to be provided when a specific area begins, the area must
know about the game to allow the game to instantiate the active dialogue.
It is suggested to use the concept of an interface here to avoid exposing the entire game to
components that need to activate a dialogue.
Implement an interface DialogHandler containing only a single method, void publish(Dialog).
Then ensure the game implements this interface and overrides the publish method to assign
the dialogue to be published as the current game dialogue.
Finally, modify the Spawn class so that:
• The area knows the game to which it belongs but only as a DialogHandler (a less
intrusive abstract view);
• On the first call to its update method, the game’s active dialogue becomes the
"welcome" dialogue. This dialogue should not reappear if the "Spawn" area is revisited
after leaving for another area.
Welcome Dialogue and ”Reset”: The GAME_RESET key restarts the game from the
beginning, so it is normal for the welcome dialogue to appear again after using this key.
However, this dialogue should not reappear when the AREA_RESET key is used.
3.2.2 Task
Once the concepts described above are implemented according to the specifications and
constraints, run your ICoop game. Verify:
1. That the introductory dialogue appears at the start of the game and can be scrolled
using the NEXT_DIALOG key;
202. That while the dialogue is displayed, the characters no longer respond to controls;
3. That once the dialogue ends, the game resumes the behavior it had before;
4. That the reset keys work as intended regarding the welcome dialogue.
3.3 Collectible Items
As they move around, the main characters can collect items that provide benefits (e.g.,
healing, invulnerability) or serve as useful equipment.
You are now required to introduce an abstract class, ICoopCollectable, modeling the default
general behavior of ”collectible” items in ICoop-type games. This class will inherit from
the CollectableAreaEntity class provided in the template (package areagame.actor). By
default, an ICoopCollectable is traversable and, as an Interactable, only allows contact
interactions at this level of abstraction (this can obviously be refined in subclasses). Finally,
an ICoopCollectable must disappear from the area when collected.
3.3.1 Collecting Explosives
Modify your code so that characters can collect explosives through contact interactions.
Guideline: Use the boolean parameter isCellInteraction to differentiate between contact
and remote interactions in the interactWith methods specific to the actors.
3.3.2 Task
Once the concepts described above are implemented according to the specifications, run
your ICoop game. Verify that explosives can now be collected through contact interaction
with the characters but only if they are neither activated nor exploding (feel free to add
them at locations of your choice). Once collected, they should simply disappear from the
area. Also, verify that remote interactions with explosives work as they did before.
3.3.3 The ElementalItem
Next, introduce a special category of ICoopCollectable. These are ”collectible” objects
serving a natural element (ElementalItem). They will behave both as ElementalEntity
and logical signals (Logic) and will not be instantiable at this level of abstraction.
The area of belonging of an ElementalItem, its starting position, orientation, and the
natural element it serves are provided at construction.
As a Logic, an ElementalItem is ”on” (isOn() returning true) when collected and ”off”
(isOff() returning true) otherwise.
To spice up interactions, ensure that an ElementalItem can only be collected by an entity
of the same elemental category (e.g., a fire item can only be collected by a fire actor).
213.3.4 The Orbs
A first concrete type of ElementalItem to implement is the orb, modeled by the Orb class.
An orb is an ElementalItem that protects the collector against specific types of damage.
A water orb provides protection against water damage, and its collection should trigger the
display of the orb_water_msg dialogue.
A fire orb provides protection against fire damage, and its collection should trigger the
display of the orb_fire_msg dialogue. Its area of belonging, the element it serves (fire or
water), and its starting position are provided as construction parameters. It is oriented
upward by default.
An orb is drawn using an animation. Refer to Appendix 7.3.3 for more detailed information.
When a character enters into contact interaction with an orb, a dialogue should appear:
"orb_water_msg" for water orbs and "orb_fire_msg" for fire orbs. Once the dialogue
ends, the orb should disappear (i.e., be collected).
Hint: An enumerated type OrbType, specifying the element served, the dialogue to display,
and the spriteYDelta (as used by the animation), might be useful here.
Question 2
How would you propose allowing an orb to communicate the message to activate in
the game without using intrusive getters?
To test this new type of object, ensure that the OrbWay area is initialized with a fire orb at
position (17,12) and a water orb at position (17,6).
3.3.5 Task
Once the concepts described above are implemented according to the specifications and
constraints, run your ICoop game. Verify that it behaves as shown in the following video:
https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step2-3.mp4; specifically:
1. That the OrbWay room contains a fire orb and a water orb at the desired positions,
both animated;
2. That the red character can collect the fire orb and the blue character can do the same
with the water orb;
3. That the specific dialogue for each orb type is displayed during contact interaction
with the characters;
4. And that the orbs disappear once the dialogues are completed.
The fact that an ElementalItem can only be collected by a character serving the same
element will be tested later in the project.
3.4 Elemental Walls
An elemental wall (ElementalWall) is a wall that behaves like an ElementalEntity. It
can be active or inactive (modeled using a Logic attribute). It can be destroyed and must
22disappear from the area if it is.
Its area of belonging, orientation, starting position, the logical signal determining whether
it is active or not, and the sprite used for drawing are provided at construction. This actor
is traversable by default and only draws itself if it is active.
As an Interactable, an ElementalWall can only engage in contact interactions. As an
Interactor, it always enforces contact interactions but not remote ones.
When it interacts with an ICoopPlayer and is active, it inflicts damage. We will consider
that the method for applying damage cannot be defined at this level of abstraction.
All sprites for drawing it can be extracted as follows:
Sprite[] wallSprites = RPGSprite.extractSprites(spriteName ,
4, 1, 1, this , Vector.ZERO , 256, 256)
To draw it specifically, use the sprite corresponding to its orientation:
wallSprites[getOrientation().ordinal()]
Two specific types of ElementalWall will be introduced:
• Fire walls, drawn using the "fire_wall" sprites, inflict fire damage on ICoopPlayer
by causing a one-point life loss;
• Water walls, drawn using the "water_wall" sprites, inflict water damage on ICoopPlayer
with the same life loss.
3.4.1 Elemental Category Role
To complicate interactions, ensure that entities of different elemental categories cannot
coexist in the same grid cell. For example, a water wall will only allow a water character to
pass, and a fire wall will only allow a fire character.
Question 3
What modifications would you propose for ICoopBehavior.ICoopCell to meet this
requirement? (the tests on the elemental category are not considered to be type tests
because we are not testing the actual nature of the instances but only one of their
behavioral facets).
To test this new type of actor, ensure that OrbWay is initialized with five fire walls oriented
left at positions (12, 10+i) and five water walls at positions (12, 4+i), where i is the
wall number, varying from 0 to 4 (see Figure 6). At this stage, they will always be active:
the associated signal will be Logic.TRUE.
Also, for testing purposes, add a water wall at position (7,12) and a fire wall at position
(7,6). Both walls will be associated with a constantly active signal.
3.4.2 Task
Once the concepts described above are implemented according to the specifications and
constraints, run your ICoop game. Verify that it behaves as shown in the following video:
23Figure 6: Les obstacles désignés par les flèches rouge et bleue sont constitués de 5 murs
juxtaposés verticalement.
https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step2-4.mp4; specifically:
1. That movement across areas remains as it was in previous steps;
2. That the OrbWay room contains fire and water walls as shown in the video;
3. That the walls only allow characters of their elemental category to pass;
4. And that the walls inflict damage on characters when they pass through.
3.5 Assistance, Healing, and Invulnerability Periods
The goal now is to equip characters with means to mitigate the damage caused by the walls.
Three methods will be provided:
• Collecting orbs to become invulnerable to specific types of damage;
• Collecting hearts to restore health points;
• Each character can help the other by deactivating their wall (introducing a cooperation
mechanism between them!).
First, complete the interaction between ICoopPlayer and orbs so that collecting an orb
makes the character invulnerable to the damage it protects against: fire orbs protect against
fire damage, and water orbs act similarly.
Next, introduce a new collectible object, the heart actor (Heart), whose contact interaction
with an ICoopPlayer restores one health point to the character (and removes the heart from
the area). A heart does not serve any natural element (there are no fire or water hearts!).
Graphically, a heart will animate according to the details in Appendix 7.3.4.
Finally, create a pressure plate actor (PressurePlate) that behaves as a logical signal
(Logic). When a character steps on it, it activates (isOn() returning true).
You have access to the "GroundPlateOff" graphic resource to create the RPGSprite for
drawing. This actor is oriented downward by default.
To test your new developments, ensure that OrbWay is initialized with four hearts at positions
of your choice (for example, at (8,4), (10,6), (5,13), and (10,11)). You will also create
a first pressure plate at position (5,7) and a second at position (5,10). Finally, modify
the construction of the walls so that the fire wall deactivates when the first pressure plate
is active and the water wall deactivates when the second pressure plate is active. This will
allow each character to help the other access the orb without taking damage from the walls.
243.5.1 Task
Once the previously described concepts have been implemented according to the specifications
and constraints provided, launch your game ICoop. Verify that it behaves as shown in
the following short video: https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step2-
5.mp4 ; specifically:
1. that the blue character can deactivate the fire wall by stepping on the pressure plate
accessible to them ;
2. that a similar behavior is observed for the red character ;
3. that the orbs can be collected without damage once the walls are deactivated ;
4. and that the walls reappear when the characters leave the pressure plate controlling
them.
3.6 Maze Area
To conclude this feature-rich step, you are tasked with coding a new area named Maze
(with the title "Maze"). In this area, you will register actors according to the configuration
provided in Appendix 7.2.
This room can be accessed from the Spawn room via a door configured as follows:
Destination Arrival Coordinates Signal Main Cell Other Cells
”Maze” (2,39), (3,39) Logic.TRUE (4,0) (5,0)
The reverse access from Maze to Spawn is not possible (no door is provided: the characters
are currently trapped in the maze area... which should add a bit of suspense!). The spawn
positions in the Maze are (2,39) for the red character and (3,39) for the blue one.
3.6.1 Task
Once the concepts described above are implemented according to the specifications and
constraints, run your ICoop game. Verify:
1. That the characters can access the maze area through the specified door and vice-versa
(as shown in the video: https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step2-
6.mp4) ;
2. That the various components appear and behave according to the specifications in
Appendix 7.2.
3.7 Validation of Step 2
To validate this step, all checks from sections 3.1.1, 3.2.2, 3.3.2, 3.3.5, 3.4.2, 3.5.1, and 3.6.1
must have been completed.
The game ICoop, whose behavior meets the validation steps above, must be
submitted at the end of the project.
254 Enemies and Battles (Step 3)
In this third part of the project, you are tasked with enriching the design by adding enemies
for the characters to face and various more ”martial” equipment to set the stage for epic
battles. A rudimentary inventory concept will be introduced, allowing characters to store
and select the equipment they use.
Below, you will find the specifications to follow, along with some guidelines.
4.1 Equipment
Characters will have equipment they can store in an inventory containing various items.
The generic notion of inventory (Inventory) and inventory item (InventoryItem) are
provided in the template (in areagame.handler of the game-engine module).
4.1.1 Inventory Items
The functional specification of an inventory item (InventoryItem, provided in game-engine)
is that it should provide access to:
• its name (a String);
• and the pocket of the inventory in which it is located (an int representing the pocket
number for simplicity).
Note that a pocket can contain multiple inventory items. For simplicity, we will only use
one pocket in this project.
An InventoryItem should be viewed as the description of an item model that can be stored
in an inventory (e.g., the item model ”sword”), rather than as a physical object. To simplify,
however, we will refer to inventory items rather than inventory item models.
ICoopItem Your ICoop game will have its own concrete implementation of inventory
items. You are required to implement the ICoopItem concept, a specialization of InventoryItem
that models the inventory items specific to the ICoop game. You can place this concept in
your game’s areagame.handler directory.
In the base version of ICoop that you are required to deliver, the possible inventory items in
the game will be: swords (Sword), fire and water keys (FireKey, WaterKey), fire and water
staffs (FireStaff and WaterStaff), and explosives (Explosive).
For now, there is no need to anticipate actors such as ”sword,” ”key,” or ”staff.” These are
simply models of inventory items.
Guidelines:
• Code ICoopItem as an enumerated type;
• An enumerated type can implement an interface.
You may freely choose the name associated with each planned item. Additionally, anticipate
that every ICoopItem will have a graphical representation and thus will be characterized by
26a Sprite. You can use the images sword.icon, key_blue, key_red, staff_water.icon,
staff_fire.icon, and explosive from the resources/images/sprites/ directory. For
simplicity, in the base version of the project, the inventory will have only one pocket, and
all items will, by default, belong to this pocket with identifier zero.
4.1.2 Inventories
An abstract inventory (class Inventory provided in game-engine) is characterized by
a set of pockets (Pocket) in which it is possible to add or remove a given quantity of
InventoryItem. Review these classes broadly to anticipate the functionalities they offer.3
The pockets in the inventory are numbered (integer indices). Note that the embedded
interface Inventory.Holder allows you to functionally designate inventory holders: you
can query them to determine if they possess a particular item using the possess method.
You will implement ICoopInventory, a specialization of Inventory specific to the ICoop
game, which by default is constructed with a single pocket named as you choose. You can
also place this concept in the areagame.handler directory of your game.
Next, you will complete the modeling of the main character ICoopPlayer to provide it
with an inventory of type ICoopInventory. At the creation of the character, a few items,
including one or more explosives and swords, will be hard-coded into the inventory for
simplicity.4 Naturally, ICoopPlayer must be listed as an inventory owner. To preserve
encapsulation, it will not expose its inventory to the outside world.
You will consider that ICoopPlayer can use only one inventory item at a time. The currently
used item will be referred to as the current equipment. You will add new functionalities to
ICoopPlayer, allowing it to:
• Switch between inventory items (i.e., change its current equipment) using the
keyBindings.switchItem() key. Switching from one item to another will be done
circularly (after the last item in the list, it returns to the first). Note that the
Inventory class provides the method contains(InventoryItem item) to check if
any of the pockets contain the item item.
• Use the current equipment using the keyBindings.useItem() key.
Hints: To make the game more user-friendly, it is preferable that inventory items always
appear in the same order when cycling through them. An attribute defining this order may
be useful. However, only items actually present in the character’s inventory should appear
in this order.
The use of the current equipment will naturally be handled on a case-by-case basis.5 Each
piece of equipment will be handled as a specific case.
For now, you will only program the use of explosives. All other cases will be considered
default cases where the character does nothing.
Using an item of type ”explosive” involves creating an ”explosive” actor in front of the
character. This creation will only be possible if the cell in front of the character allows the
3The embedded GUI interface is not required in the basic version of the project.
4You can enhance this point in the extensions section of the project.
5Enumerated types are very well-suited for use with a switch statement.
27Figure 7: Graphical display areas for character status (one area per character)
placement of an actor. A bomb used must be removed from the inventory (the quantity of
this item in the inventory will decrease by one).
The explosive will be endowed with additional interactions:
• With other explosives, causing them to explode.
• With walls, destroying them.
4.1.3 Collection and Inventory
In the previous stage, three types of ”collectible” actors were introduced: explosives, orbs,
and hearts. A distinction must now be made between them: some collected objects can be
stored in the inventory (as part of the equipment), while others cannot. Typically, orbs and
hearts, once collected, will not appear in the inventory, while explosives will. You are asked
to adapt your code to reflect this behavior.
4.1.4 Graphics
To visualize character-specific information (such as the current inventory equipment), you
are now asked to use and complete the provided class ICoopPlayerStatusGUI. This class
models a ”graphical object describing the status of a main character”. The provided template
allows you to draw the small brown circle used as a support for inventory items in Figure 7
(the graphical object gearDisplay); you can enhance it to add other information as needed.
Complete the existing drawing method of this class to display the sprite of the current
inventory item of each character within the brown circle. Specifically, you need to construct
and draw an image:
new ImageGraphics(ResourcePath.getSprite(sprite_name), 0.5f,
0.5f, new RegionOfInterest(0, 0, 16, 16), anchor.add(new
Vector (0.5f, height - 1.25f)), 1, DEPTH);
28where sprite_name is the name of the sprite associated with the ICoopItem selected by the
corresponding character.
Note: If the flipped parameter of an ICoopPlayerStatusGUI is true then its draw
method will display it on the right else on the left.
4.1.5 Task
Once the concepts described above are implemented according to the specifications and
constraints provided, launch your ICoop game. Verify the following:
1. That it is possible to switch between equipment items using the specific keys for each
character (default Q and U), and that the current equipment is displayed correctly.
2. That only explicitly added equipment items appear in the inventory when cycling
through items using the Tab key.
3. That only the current equipment can be used (verify this with the explosive) using
the specific keys for each character (default E and O).
4. That an explosive, when detonated, triggers the explosion of nearby explosives and
destroys walls.
5. That it can no longer be selected as current equipment if there was only one and the
character has used it.
6. That if the inventory is empty of explosives for a character and they collect one, the
item reappears in the inventory.
7. And that the graphical description of the character’s status remains intact when
transitioning between areas.
In the mandatory part of the project, it is not required that a ”reset” of the
current area restores the inventory state to what it was when the area was first
entered. For example, if an object is collected into the inventory and the area is
later reset, the prior collection is not expected to be undone. You can address
this issue in the extensions if desired.
4.2 Enemies
Enemies (Foe) are actors with the following characteristics:
• They can move on a grid.
• They have health points and a maximum number of health points.
• They die if their health points are less than or equal to zero; they then disappear from
the game area after a specific visual effect. This involves an animation showing them
vanishing, which is the same for all enemy types ("icoop/vanish").
• They can be vulnerable to certain types of damage (they have a ”list of vulnerabilities”).
•
They actively request interactions (not just receive them).
• By default, they can engage in both contact and remote interactions.
29Figure 8: Fire-throwing skulls
• They can only be walked on if they are dead.
• Like characters, they can lose health points due to a specific type of damage.
• After losing health points (regardless of the cause), they have a short immunity period,
the duration of which is the same for all enemies.
The maximum number of health points cannot be defined at this level of abstraction but
must be accessible for all enemy types.
The possible vulnerabilities are the same as for the characters (vulnerabilities to physical,
fire, or water damage).
Each type of enemy has a specific list of vulnerabilities defined at construction (e.g., firethrowing
skulls are vulnerable to water and physical damage but not fire). An enemy starts
with the maximum number of health points.
Enemies disappear from the game area when they die (life points less than or equal to zero).
A short animation plays just before they disappear (see 7.3.8).
Various enemy types are possible. You are required to implement two specific enemy types
described below: ”fire-throwing skulls” and ”bombers” (explosive placers)—an exciting
challenge! :-)
Hints for this section:
• Using enumerated types combined with the switch statement is a natural way to
implement actor updates (update) based on their states (as will be the case for most
enemies).
• Ensure that each class contains only what is specific to it and that superclasses are
unaware of their subclasses.
• The code for interactWith methods should, in most cases, be concise. If your code
becomes too verbose, please consult us, as it might indicate a misunderstanding of
how to implement interactions.
Since some enemies will launch projectiles (e.g., the fire-throwing skulls in Figure 8), you
are first asked to model this concept.
304.2.1 Projectiles
A projectile is a mobile grid-based actor that actively requests interactions. It can fly over
grid cells and can be walked on.
A projectile is characterized by its movement speed and the maximum distance it can travel
from its starting point. These two characteristics are fixed at construction. It moves in
a single direction (its orientation) and disappears when it completes its course (reaches
its maximum distance). The speed attribute modulates movement speed (e.g., using a
statement like move(MOVE_DURATION/speed)). The maximum distance can be implemented
as an integer. At each simulation cycle, the remaining distance is decremented by one.
By default, a projectile does not accept contact or remote interactions. However, it can
impose contact interactions as long as it is in motion (everything it touches along its path
can be affected).
A projectile can also be stopped mid-course before reaching its maximum distance.
At this abstraction level, interactions with specific concrete actors cannot yet be defined.
As a first concrete projectile type, implement flames.
Hint: You can introduce a category Unstoppable to model entities that can fly over cells
(including those usually unwalkable). Then, modify ICoopBehaviour and ICoopCell so
that an Unstoppable entity can occupy any type of cell.
Flames A flame (Fire) is a projectile that disappears when stopped mid-course. It is
visually represented by an animation (see 7.3.6).
Through contact interaction:
• It attempts to cause fire damage to all enemies and ICoopPlayer characters (e.g., 1
point of damage, though you are free to choose a different value). This is an attempt
because damage will not be inflicted if the targeted entity is immune to fire damage.
• It triggers the explosion of explosives.
4.2.2 Fire-throwing Skulls
A fire-throwing skull (HellSkull) is an enemy vulnerable to physical and water damage.
Its maximum health points are constant across all enemies of this type (e.g., 1 for simpler
battles, though you can choose another value). They can be traversed. They impose contact
interactions (if alive) but not remote ones.
The graphical representation of a HellSkull can be achieved with an animation (see 7.3.9).
The HellSkull can launch ”flames” (see Figure 8).
General Behavior At random intervals t, the HellSkull generates a flame in front of it
(if the space is accessible). The interval t is randomly regenerated after each launch, chosen
between two bounds you specify (e.g., 0.5f and 2.f).
To generate a random float between two bounds MIN and MAX, use:
31float randomFloat =
RandomGenerator.getInstance().nextFloat(MIN , MAX);
Interactions In contact interactions, the HellSkull inflicts fire damage, causing the loss
of a specific number of health points for the main characters. The amount of damage is
constant across all HellSkull instances and, for simplicity, is the same regardless of the
interacting entity.
For testing, ensure the Maze area contains 10 HellSkull instances oriented to the right, positioned
at coordinates (12,33), (12,31), (12,29), (12,27), (12,25), (10,33), (10,32),
(10,30), (10,28), and (10,26).
Some debugging tips (especially for the Maze area):
• You can zoom out by adjusting the DEFAULT_SCALE_FACTOR in ICoopArea
(try a value like 39.f instead of 13.f).
• Artificially position the center of mass on a region to observe (e.g., for
debugging BombFoe, temporarily change the ”spawn” positions of the
characters to (3,10) and (4,10)).
• Comment out the character death handling code to observe enemy
behavior more easily ;-).
4.2.3 Task
Once the previously described concepts are implemented according to the specifications and
constraints provided, launch your game ICoop. You will verify:
1. that the 10 fire-throwing skulls appear at the specified positions;
2. that all of them launch flames at random intervals;
3. that the flames move in front of the skulls launching them, fly over all cells (even those
not walkable), animate, and disappear when they reach the edge of the area;
4. that flames launched by the top-left skull do not damage the skull aligned to its right
(this skull never disappears);
5. that explosions caused by explosives do not damage the fire-throwing skulls;
6. that the fire-throwing skulls inflict fire damage to characters on direct contact;
7. that the flames also inflict fire damage to the characters;
8. and that the launched projectiles destroy everything in their path as long as their
course is not complete and disappear by themselves once their course ends.
4.2.4 Bomber
The bomber (BombFoe, see Figure 9) is an enemy vulnerable to physical and fire damage.
The maximum number of health points is the same for all its instances (e.g., 2). It is created
oriented downward.
It can be in different states that determine its behavior. By default, it is in the ”idle” state.
In this state, the bomber does nothing except move. Otherwise, it can either be attacking
32Figure 9: Bombers (explosive-placing enemies)
or protecting itself.
The graphical representation of the bomber will be achieved using specific animations:
"icoop/bombMonster" for the general case and "icoop/bombMonster.protecting" when
it is protecting itself.
Interactions The bomber requests remote interactions but does not engage in contact
interactions. It can only interact when idle or attacking. While attacking, its interaction
(perception) field is the single cell in front of it. Otherwise, its extended perception field is a
constant set of cells in front of it (the same number for all bombers, e.g., 8). It is represented
using an animation (see 7.3.10).
The bomber interacts with the main characters. The interaction consists of switching to
”attack” mode when one of these characters is within its interaction field. For now, you do
not need to address how the main characters fight their enemies.
General Behavior The bomber has idle moments during which it does absolutely nothing
(not even moving), regardless of its state. This idle time is initially zero and is coded as
a number of simulation steps. It cannot exceed a certain value (e.g., 24, common to all
enemies of this type).
The bomber’s behavior is as follows when its idle time has elapsed, and it is not in an
immunity period:
• If it is in the idle state and no movement is in progress, it moves randomly (see below)
and may enter an idle moment with a duration randomly drawn between zero and the
maximum idle time.
• If it is in ”attack” mode, it moves toward the character it has memorized as its target
until it is sufficiently close (e.g., a distance of 36
). More details are provided below. It
then drops an explosive in the cell in front of it (if free) and switches to ”protection”
mode.
• In the ”protection” state, it moves more slowly (you are free to implement this as you
wish) and forgets its target. It remains in this state for a certain period (a random
6The distance can be calculated using the distanceBetween method from DiscreteCoordinates
33duration between two class-specific constants, e.g., 72 and 120) before switching back
to the idle state.
When in an immunity period, the bomber switches to idle mode with zero idle time.
Random Movement The random movement of the bomber is somewhat similar to that
of a main character. However, this movement is not controlled via the keyboard, and the
change in orientation occurs randomly. The probability of changing orientation is drawn
randomly, and if it occurs, the new orientation is also chosen randomly from the four possible
directions.
For example, the bomber has a 40% chance of changing orientation, and if it does, it will
choose a direction randomly and equally likely among the four options. To randomly draw
an integer between 0 and MAX, use the following instruction:
int randomInt = RandomGenerator.getInstance().nextInt(MAX);
To adopt the desired behavior in 40% of cases, you can randomly draw a double:
double randDouble = RandomGenerator.getInstance().nextDouble();
and apply the behavior if the number drawn is less than 0.4.
Note: The call to the move method can be parameterized with a value ANIMATION_DURATION
/ speedFactor. The speedFactor can vary depending on the bomber’s state (e.g., 2 for
normal animation speed, 6 for fast, and 1 for slow). Random movement steps will occur at
normal animation speed.
Targeted Movement The following simple algorithm will be used to choose the direction:
i. Calculate v, the vector separating the bomber from its target (method sub from
Vector), deltaX, the x-component of this vector, and deltaY, its y-component.
ii. If the absolute value of deltaX is greater than that of deltaY (the bomber is farther
horizontally than vertically from its target), orient the bomber according to
the vector (deltaX, 0); otherwise, orient it according to the vector (0, deltaY).
The Orientation.fromVector method can be used to convert a Vector into an
Orientation;
iii. If the orientation change was not successful, a rapid movement step will occur.
To test these developments, ensure that the Maze area contains 4 BombFoe positioned at the
coordinates (5,15), (6,17), (10,17), and (5,14).
4.2.5 Task
Once the previously described concepts are implemented according to the specifications
and constraints provided, launch your game ICoop. Verify that it behaves as shown in
the following video: https://proginsc.epfl.ch/wwwhiver/mini-projet2/videos/icoop/step3-
3.mp4; specifically:
1. The BombFoe moves on its own, randomly changing direction within the same limits
as the characters;
342. It can randomly drop bombs when a character enters its extended perception field;
3. It enters protection mode with a specific graphic when it successfully drops a bomb;
4. It randomly observes idle moments.
4.3 Battles
It is time to better equip the characters to defend themselves against their enemies. Start
by equipping them with a new weapon: a staff capable of launching water or fire orbs. Red
staffs will launch fire orbs, and blue staffs will launch water orbs.
4.3.1 Staffs and Water/Fire Orbs
A staff (Staff) is an ElementalItem represented by an 8-frame animation where each frame
i can be constructed as follows:
new RPGSprite(spriteName , 2, 2, this , new RegionOfInterest(i *
32, 0, 32, 32), new Vector(-0.5f, 0));
where spriteName is staff_water for blue staffs and staff_fire for red staffs. The
animation duration can be 32 (4 per frame).
Water and fire orbs are coded similarly to flames. They are also drawn using animations
(see 7.3.7). Fire or water orbs will cause 1 point of damage of their respective type to
enemies, make bombs explode, and destroy rocks. They will stop moving upon impact.
Finally, once collected by characters, staffs must be added to the inventory.
Create a red staff at position (13,2) in the Maze area and a blue staff at position (8,2).
4.3.2 Tasks
Verify that the staffs appear in the Maze area, can be collected, but only by characters of the
corresponding color, and appear in the inventory afterward. The functionality for launching
water and fire orbs will be tested later.
4.3.3 Weapon Usage by Characters
The method managing the use of equipment for ICoopPlayer currently only handles bombs.
You are required to extend it to handle swords and staffs.
To do this, model the concept that the character can also have different states determining
their behavior (e.g., if in the ”attacking with a sword” state, they can attack an enemy).
Anticipated states include the ”idle” state, similar to that described earlier for some enemies,
and states representing the character attacking: ”attacking with a sword” and ”attacking
with a staff.”
The transition from one state to another is conditioned by the call to the method managing
equipment usage (invocable via predefined controls) and, of course, by the selected equipment.
For example, the character transitions to the ”attacking with a sword” state when
35Figure 10: Attack states for the character with specific visuals: left for sword attack and
right for staff attack.
the player selects the sword as the current equipment and expresses the intention to use it
by pressing the designated key (default: E and O).
Transitioning to an attack mode can only occur if the character is in an idle state (e.g.,
the character cannot transition directly from ”attacking with a sword” to ”attacking with
a staff”). This transition should be accompanied by a specific visual (see figure 10 and
appendix 7.3.1).
Returning to the idle state should occur after the animations related to the transition to a
new state are completed and the attacks are executed:
• A sword attack ends when the user stops pressing the key corresponding to the
equipment’s usage. The character returns to the idle state as soon as the requested
interaction is completed, including the sword attack animation time.
• A staff attack ends similarly. Before transitioning to the idle state, ensure that a water
or fire orb is launched by the character, depending on their color.
You are required to modify the ICoopPlayer update method to integrate the transition to
new states (in accordance with the descriptions above). The ICoopPlayer will only move
as it previously did when in the idle state.
You will also modify the code so that:
• The character can have a remote interaction as long as they are using their sword (i.e.,
while in the ”attacking with a sword” state, remote interaction is possible).
• During a sword attack, the character can deal physical damage to enemies.
• The character can launch water/fire balls while attacking with a staff.
• Fire/water balls deal fire/water damage to enemies and destroy rocks.
4.3.4 Tasks
Once the previously described concepts are implemented according to the specifications and
constraints provided, launch your game ICoop. Verify:
1. That the main characters can still move under keyboard control when idle.
2. That they can transition to different attack modes upon request after selecting the
36appropriate equipment, whether staff or sword.
3. That the chosen equipment can be used via the anticipated keys, and this action is
reflected visually.
4. That they can launch water projectiles using the staff and that these projectiles stop
in their tracks when encountering enemies or rocks.
5. That they can destroy enemies by dealing physical damage (e.g., striking them with
the sword).
6. That they can deal physical damage to fire skulls and bombers (using the sword).
7. That they can deal water damage (using the staff) to enemies.
8. And that enemies disappear in a small puff of smoke upon dying.
4.4 Validation of Step 3
To validate this step, all the verifications from sections 4.1.5, 4.2.3, 4.2.5, 4.3.2, and 4.3.4
must be completed.
The game ICoop with the behavior described above must be submitted at the
end of the project.
37Figure 11: Passageway from Maze (left) to Arena (right): the Arena area does not allow you
to go back to Maze and is strewn with obstacles that cannot be crossed and rocks that can
be destroyed
.
5 Final challenge (Step 4)
This last step is much more open. You will only be given a specification of the features to
be implemented and a few guidelines.
You have to implement the fact that when the characters leave the Maze area through the
door in figure 11, they find themselves trapped in an area without a door, the Arena area.
Here they must destroy rocks to collect the keys of their colour. Once each character has
collected the key of their colour (elemental category), a teleporter will appear which will
take them back to the initial room, Spawn.
If they have collected the sticks of their own colour in the previous area, they will be able
to ‘open’ the door to the mansion and earn the final reward.
The implementation of this step must be based on the notion of a logical signal
(Logic): for example, the collection of the two keys can be modelled as a logical
‘and’ object (AND in the logic package of game-engine) on the two keys which
are themselves Logic objects.
385.1 Keys and Teleporters
A key is an ElementalItem that is drawn using the sprite:
new Sprite‘(icoop/’key_red , 0.6f, 0.6f, this);
if it serves the fire element or
new Sprite‘(icoop/’key_blue , 0.6f, 0.6f, this);
if it serves the water element.
A teleporter is nothing other than a drawable door which only occupies the position of its
main cell (as returned by getCurrentMainCellCoordinates()). It is only drawn if it is
active. As a ‘sprite’ for drawing it, you can use:
RPGSprite‘(’shadow , 1, 1, this , new RegionOfInterest(0, 0, 32,
32))
5.2 Area Arena
The Arena area is accessible from Maze via a door with the following specification:
destination arrival coord. Signal main cell other coord.
”Arena” (4,5),(14,15) Logic.TRUE (19,6) (19,7)
At its creation, it contains a red key in position (9,16), a blue key in position (9,4) and
a gate (closed and therefore invisible) in position (10,10). The portal will provide the
transition to the Spawn area (the characters will arrive at positions of your choice).
The Arena area is littered with rocks and obstacles, and it would be tedious to record them
all manually when creating the area. You are therefore asked to modify the code so that
the grid can dictate to the area the creation of an actor Rock wherever there is a cell of type
ROCK and the creation of an actor Obstacle wherever there is a cell of type OBSTACLE.
Be sure to code this without an intrusive getter!
5.3 Gateway to the manor
We suggest that you finalise the mandatory part of the project by associating the location
of the manor door in the Spawn area with a new type of door that causes a dialogue to
open when the characters try to interact with it by touching it. The dialogue will be
"key_required" if the door is closed and "victory" if it is open. Figure 12 shows the first
type of dialogue (corresponding to a door which is still closed).
The door will occupy the position (6,11) to coincide with the graphics. As you will stop at
this point for the development of the game itself, the door will not have to allow characters
39Figure 12: Gateway to the manor: the objective of the game, in its mandatory part, is to
succeed in opening it!
to transit to a new area (which you can correct in the extensions if you wish). You can
therefore choose the room Spawn itself as the destination room.
The characters will have succeeded in their mission when they are able to open the door.
To do this, you’ll need to extend your code to model the fact that:
• areas have a specific challenge to complete. For the area Maze the challenge is to
collect the two sticks. For the Arena area, the challenge is to collect the two keys and
for the Spawn area, the challenge is to open the manor door. For the OrbWay area, it’s
up to you (and an area can always be considered to have a successful challenge).
• The behaviour of players in an area can be conditioned by external signals. For
example, the opening of the manor’s door depends on the successful completion of the
challenges in the Arena and Maze areas.
Indication: the areas can behave like signals (Logic)!
Be sure to code this:
• without the areas having to know about each other (there is no reason for
one area to be aware of the existence of another area in the game);
• and without the characters having to memorize information that is too
specific (as an example, the knowledge of whether a stick or key has been
collected); it should be possible to change the nature of an area’s challenge
without this having any impact on the characters’ code.
5.4 Task
You are asked to code the concepts described above in accordance with the specifications
and constraints given. You will then check:
1. that the door to the manor is initially closed in the Spawn area; the dialogue indicating
that the door is closed must be displayed when the characters try to go through it;
2. that the new door in the Maze area should be able to lead to the Arena area;
403. that once through, both characters are initially trapped there;
4. that the area Arena appears as in figure 11;
5. that the characters can destroy the rocks (using the stick) but not the obstacles;
6. that each of them can collect the key of their colour once they have accessed it;
7. that the collection of the two keys causes a portal to appear and that as soon as one
of the characters passes through it (by contact), both characters are released from the
arena and teleported to the area Spawn;
8. and that the door to the mansion is opened there if the characters have also collected
the sticks in Maze.
There’s a slight difficulty to be expected here concerning the dialogue associated with the
manor door: the dialogue appears during a contact interaction and it’s not possible to move
the character until the dialogue has fully unfolded. However, when the dialogue ends, the
character will still be interacting with the door and the dialogue will start all over again.
Question 4
How do you propose to solve this little problem without introducing specific tests into
the game on the nature of the current dialogue?
5.5 Validation of Step 4
To validate this step, all the checks in section 5.4 must have been carried out.
The game ICoop whose behaviour is described above should be submitted at the
end of the project.
416 Extensions (Step 5)
To obtain bonus points to compensate for any shortcomings in the mandatory part of the
project, or to take part in the competition, you can implement a few freely chosen extensions.
A maximum of 15 points will be awarded (coding a lot of extensions to compensate for the
weaknesses of the previous parts is therefore not an option).
Implementation is open and there is very little guidance. Only a few suggestions and
indications are given below. Note that from a graphics point of view, two additional rooms
are provided to allow you to code extensions (SanctumEntrance and Sanctum). An estimate
of the points awarded for the suggested extensions is given, but don’t hesitate to ask us for
a more precise evaluation if you have a particular idea. A small bonus will be awarded for
displays of creative game design.
请加QQ:99515681 邮箱:99515681@qq.com WX:codinghelp