Skip to content

Commit

Permalink
Add cutscene feature
Browse files Browse the repository at this point in the history
  • Loading branch information
hdescottes committed Jun 6, 2024
1 parent 87f9523 commit b182d52
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ graph TD;

- [ ] crafting feature

- [ ] cutscenes feature
- [ ] credits

- [ ] improve AI

Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/com/gdx/game/map/MapManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ public void clearCurrentSelectedMapEntity() {
currentSelectedEntity = null;
}

public void disableCurrentMapMusic(){
currentMap.unloadMusic();
}

public void setPlayer(Entity entity) {
this.player = entity;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.gdx.game.entities.player.CharacterRecord;
import com.gdx.game.manager.ResourceManager;
import com.gdx.game.profile.ProfileManager;
import com.gdx.game.screen.cutscene.CreatorIntroScreen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -99,7 +100,7 @@ public void clicked(InputEvent event, float x, float y) {

gdxGame.setGameScreen(new GameScreen(gdxGame, resourceManager));
LOGGER.info("Character {} selected", playerImage.getEntity().getEntityConfig().getEntityID());
setScreenWithTransition((BaseScreen) gdxGame.getScreen(), gdxGame.getGameScreen(), new ArrayList<>());
setScreenWithTransition((BaseScreen) gdxGame.getScreen(), new CreatorIntroScreen(gdxGame, resourceManager), new ArrayList<>());
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.gdx.game.screen.cutscene;

import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
import com.gdx.game.GdxGame;
import com.gdx.game.animation.AnimatedImage;
import com.gdx.game.entities.Entity;
import com.gdx.game.entities.EntityFactory;
import com.gdx.game.manager.ResourceManager;
import com.gdx.game.map.MapFactory;

public class CreatorIntroScreen extends CutSceneBaseScreen {
private Action setupScene01;
private AnimatedImage creator;

public CreatorIntroScreen(GdxGame game, ResourceManager resourceManager) {
super(game, resourceManager);

creator = getAnimatedImage(EntityFactory.EntityType.THIEF);
creator.setName("Creator");

setupScene01 = new RunnableAction() {
@Override
public void run() {
hideMessage();
mapManager.loadMap(MapFactory.MapType.TOPPLE);
mapManager.disableCurrentMapMusic();
setCameraPosition(17, 10);

creator.setCurrentAnimation(Entity.AnimationType.WALK_UP);
creator.setVisible(true);
creator.setPosition(17, 0);
}
};

getStage().addActor(creator);
}

Action getCutsceneAction() {
setupScene01.reset();
getSwitchScreenAction().reset();

return Actions.sequence(
Actions.addAction(setupScene01),
Actions.delay(1),
Actions.addAction(Actions.moveTo(17, 10, 5, Interpolation.linear), creator),
Actions.delay(Float.parseFloat("2.5")),
Actions.addAction(Actions.run(() -> creator.setCurrentAnimation(Entity.AnimationType.IMMOBILE))),
Actions.run(() -> showMessage(creator, "Hello adventurer! Welcome to my game, or at least my prototype game!")),
Actions.delay(5),
Actions.run(() -> showMessage(creator, "Many thanks for your interest in my project, i hope you will like it.")),
Actions.delay(5),
Actions.run(() -> showMessage(creator, "Do not hesitate to contribute or suggest any idea that you might have. Help is always welcome!")),
Actions.delay(5),
Actions.run(() -> showMessage(creator, "Have fun :)")),
Actions.delay(3),
Actions.after(getSwitchScreenAction())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.gdx.game.screen.cutscene;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.gdx.game.GdxGame;
import com.gdx.game.animation.AnimatedImage;
import com.gdx.game.entities.Entity;
import com.gdx.game.entities.EntityFactory;
import com.gdx.game.manager.ResourceManager;
import com.gdx.game.map.Map;
import com.gdx.game.profile.ProfileManager;
import com.gdx.game.screen.BaseScreen;
import com.gdx.game.screen.GameScreen;
import com.gdx.game.screen.transition.effects.FadeOutTransitionEffect;
import com.gdx.game.screen.transition.effects.TransitionEffect;

import java.util.ArrayList;

public abstract class CutSceneBaseScreen extends GameScreen {
private Stage stage;
private Viewport viewport;
private Stage UIStage;
private Viewport UIViewport;
private Actor followingActor;
private Dialog messageBoxUI;
private Label label;
private boolean isCameraFixed = true;
private Action switchScreenAction;

public CutSceneBaseScreen(GdxGame game, ResourceManager resourceManager) {
super(game, resourceManager);

viewport = new ScreenViewport(camera);
stage = new Stage(viewport);

UIViewport = new ScreenViewport(hudCamera);
UIStage = new Stage(UIViewport);

label = new Label("", ResourceManager.skin);
label.setWrap(true);

messageBoxUI = new Dialog("", ResourceManager.skin);
messageBoxUI.setVisible(false);
messageBoxUI.getContentTable().add(label).width(stage.getWidth()/2).pad(10, 10, 10, 0);
messageBoxUI.pack();
messageBoxUI.setPosition(stage.getWidth() / 2 - messageBoxUI.getWidth() / 2, stage.getHeight() - messageBoxUI.getHeight());

followingActor = new Actor();
followingActor.setPosition(0, 0);

//Actions
switchScreenAction = new RunnableAction(){
@Override
public void run() {
ArrayList<TransitionEffect> effects = new ArrayList<>();
effects.add(new FadeOutTransitionEffect(1f));
setScreenWithTransition((BaseScreen) gdxGame.getScreen(), gdxGame.getGameScreen(), effects);
}
};

UIStage.addActor(messageBoxUI);
}

abstract Action getCutsceneAction();

AnimatedImage getAnimatedImage(EntityFactory.EntityType entityType){
Entity entity = EntityFactory.getInstance().getEntity(entityType);
return setEntityAnimation(entity);
}

private AnimatedImage setEntityAnimation(Entity entity){
final AnimatedImage animEntity = new AnimatedImage();
animEntity.setEntity(entity);
animEntity.setWidth(17);
animEntity.setHeight(17);
animEntity.setSize(animEntity.getWidth() * Map.UNIT_SCALE, animEntity.getHeight() * Map.UNIT_SCALE);
return animEntity;
}

void setCameraPosition(float x, float y){
camera.position.set(x, y, 0f);
isCameraFixed = true;
}

void showMessage(AnimatedImage animatedImage, String message){
label.setText(message);
messageBoxUI.getTitleLabel().setText(animatedImage.getName());
messageBoxUI.pack();
messageBoxUI.setVisible(true);
}

void hideMessage(){
messageBoxUI.setVisible(false);
}

@Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

mapRenderer.setView(camera);

mapRenderer.getBatch().enableBlending();
mapRenderer.getBatch().setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

if (mapManager.hasMapChanged()) {
mapRenderer.setMap(mapManager.getCurrentTiledMap());
mapManager.setMapChanged(false);
}

mapRenderer.render();

if (!isCameraFixed) {
camera.position.set(followingActor.getX(), followingActor.getY(), 0f);
}
camera.update();

UIStage.act(delta);
UIStage.draw();

stage.act(delta);
stage.draw();
}

@Override
public void show() {
stage.addAction(getCutsceneAction());
ProfileManager.getInstance().removeAllObservers();
if (mapRenderer == null) {
mapRenderer = new OrthogonalTiledMapRenderer(mapManager.getCurrentTiledMap(), Map.UNIT_SCALE);
}
}

@Override
public void hide() {
ProfileManager.getInstance().removeAllObservers();
Gdx.input.setInputProcessor(null);
}

@Override
public Stage getStage() {
return stage;
}

public Action getSwitchScreenAction() {
return switchScreenAction;
}
}
2 changes: 1 addition & 1 deletion core/src/test/java/com/gdx/game/battle/BattleHUDTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void add_entity_battle_step(BattleObserver.BattleEvent event, int actorNumber, E
private static Stream<Arguments> addEntitySteps() {
return Stream.of(
Arguments.of(BattleObserver.BattleEvent.PLAYER_ADDED, 0, Entity.AnimationType.WALK_RIGHT),
Arguments.of(BattleObserver.BattleEvent.OPPONENT_ADDED, 1, Entity.AnimationType.IDLE)
Arguments.of(BattleObserver.BattleEvent.OPPONENT_ADDED, 1, Entity.AnimationType.IMMOBILE)
);
}

Expand Down
25 changes: 23 additions & 2 deletions core/src/test/java/com/gdx/game/dialog/ConversationUITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.JsonValue;
import com.gdx.game.GdxRunner;
import com.gdx.game.entities.EntityConfig;
import com.gdx.game.manager.ResourceManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

Expand Down Expand Up @@ -40,7 +45,23 @@ public void testLoadConversation_ShouldSucceed() {

conversationUI.loadConversation(config);

assertThat(conversationUI.getCurrentEntityID()).isEqualTo("TOWN_FOLK1");
assertThat(conversationUI.getCurrentConversationGraph().getCurrentConversationID()).isEqualTo("1");
assertThat("TOWN_FOLK1").isEqualTo(conversationUI.getCurrentEntityID());
assertThat("1").isEqualTo(conversationUI.getCurrentConversationGraph().getCurrentConversationID());
}

@Test
public void testLoadCourtesyConversation_ShouldSucceed() {
Json json = new Json();
String value = "{animationConfig:[{texturePaths:[sprites/characters/Player0.png,sprites/characters/Player1.png],gridPoints:[{x:3},{x:3}]},{animationType:IMMOBILE,texturePaths:[sprites/characters/Player0.png,sprites/characters/Player1.png],gridPoints:[{x:3},{x:3}]}],state:IMMOBILE,entityID:TOWN_INNKEEPER,conversationConfigPath:\"\",questConfigPath:\"\",currentQuestID:\"\",itemTypeID:NONE}";
EntityConfig config = json.fromJson(EntityConfig.class, value);
ConversationUI conversationUI = new ConversationUI();
ConversationGraph graph = json.fromJson(ConversationGraph.class, Gdx.files.internal("conversations/conversation_courtesy.json"));

conversationUI.loadConversation(config);
String conversationID = conversationUI.getCurrentConversationGraph().getCurrentConversationID();
String dialog = graph.getConversations().get(conversationID).getDialog();

assertThat("TOWN_INNKEEPER").isEqualTo(conversationUI.getCurrentEntityID());
assertThat(dialog).isEqualTo(conversationUI.getCurrentConversationGraph().displayCurrentConversation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.gdx.game.screen.cutscene;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.gdx.game.GdxGame;
import com.gdx.game.GdxRunner;
import com.gdx.game.entities.EntityFactory;
import com.gdx.game.entities.player.PlayerGraphicsComponent;
import com.gdx.game.manager.ResourceManager;
import com.gdx.game.profile.ProfileManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedConstruction;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;

@ExtendWith(GdxRunner.class)
public class CreatorIntroScreenTest {

private MockedConstruction<PlayerGraphicsComponent> mockPlayerGraphics;
private MockedConstruction<ShapeRenderer> mockShapeRenderer;
private MockedConstruction<SpriteBatch> mockSpriteBatch;
private MockedConstruction<Stage> mockStage;

@BeforeEach
void init() {
Gdx.gl = mock(GL20.class);
Gdx.gl20 = mock(GL20.class);
mockPlayerGraphics = mockConstruction(PlayerGraphicsComponent.class);
mockShapeRenderer = mockConstruction(ShapeRenderer.class);
mockSpriteBatch = mockConstruction(SpriteBatch.class);
mockStage = mockConstruction(Stage.class);
ProfileManager profileManager = ProfileManager.getInstance();
profileManager.setProperty("playerCharacter", EntityFactory.EntityType.WARRIOR);
profileManager.setProperty("currentPlayerCharacterAP", 15);
profileManager.setProperty("currentPlayerCharacterDP", 15);
profileManager.setProperty("currentPlayerCharacterSPDP", 10);
}

@AfterEach
void end() {
mockPlayerGraphics.close();
mockShapeRenderer.close();
mockSpriteBatch.close();
mockStage.close();
}

@Test
void screen_instance() {
GdxGame gdxGame = mock(GdxGame.class);
ResourceManager resourceManager = new ResourceManager();
CreatorIntroScreen screen = new CreatorIntroScreen(gdxGame, resourceManager);

assertNotNull(screen);
}
}

0 comments on commit b182d52

Please sign in to comment.