From ad2e2ac76a3341ffab1fb05c16ef858528299d1e Mon Sep 17 00:00:00 2001 From: Magpie Date: Fri, 15 Apr 2022 08:36:59 +0200 Subject: [PATCH] Big map update 8 Dialog updates. - Dialogs can be assigned to enemies. - Dialogs have a few more conditions and effects. - Dummy objects that can be removed from dialogs by ID. - Dialogs can force a battle by ID. --- .../adventure/character/DialogActor.java | 103 ++------------- .../adventure/character/DummySprite.java | 24 ++++ .../adventure/character/EnemySprite.java | 2 + .../forge/adventure/character/MapActor.java | 4 + .../src/forge/adventure/data/DialogData.java | 9 +- .../src/forge/adventure/stage/MapStage.java | 101 ++++++++------ .../src/forge/adventure/util/MapDialog.java | 125 ++++++++++++++++++ .../Shandalar/maps/map/debug_map.tmx | 62 ++++++++- 8 files changed, 288 insertions(+), 142 deletions(-) create mode 100644 forge-gui-mobile/src/forge/adventure/character/DummySprite.java create mode 100644 forge-gui-mobile/src/forge/adventure/util/MapDialog.java diff --git a/forge-gui-mobile/src/forge/adventure/character/DialogActor.java b/forge-gui-mobile/src/forge/adventure/character/DialogActor.java index 61d830a3d32..c2bc90e6286 100644 --- a/forge-gui-mobile/src/forge/adventure/character/DialogActor.java +++ b/forge-gui-mobile/src/forge/adventure/character/DialogActor.java @@ -1,123 +1,36 @@ package forge.adventure.character; -import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Json; -import com.badlogic.gdx.utils.SerializationException; -import forge.Forge; -import forge.adventure.data.DialogData; -import forge.adventure.data.RewardData; import forge.adventure.stage.MapStage; -import forge.adventure.util.Config; -import forge.adventure.util.Controls; -import forge.adventure.util.Current; +import forge.adventure.util.MapDialog; /** * Map actor that will show a text message with optional choices */ -public class DialogActor extends MapActor{ +public class DialogActor extends MapActor { private final MapStage stage; - private final String dialogJSON; private final TextureRegion textureRegion; - private final String defaultJSON = "[\n" + - " {\n" + - " \"effect\":[],\n" + - " \"name\":\"Error\",\n" + - " \"text\":\"This is a fallback dialog.\\nPlease check Forge logs for errors.\",\n" + - " \"condition\":[],\n" + - " \"options\":[\n" + - " { \"name\":\"OK\" }\n" + - " ]\n" + - " }\n" + - "]"; + private final MapDialog dialog; - - public DialogActor(MapStage stage, int id, String dialog, TextureRegion textureRegion) { + public DialogActor(MapStage stage, int id, String S, TextureRegion textureRegion) { super(id); this.stage = stage; - if (!dialog.isEmpty()){ - System.err.printf("Dialog error. Dialog property is empty.\n"); - this.dialogJSON = defaultJSON; - } - else this.dialogJSON = dialog; + dialog = new MapDialog(S, stage, id); this.textureRegion = textureRegion; } @Override public void onPlayerCollide() { - Json json = new Json(); - Array data; stage.resetPosition(); stage.showDialog(); - - try { data = json.fromJson(Array.class, DialogData.class, dialogJSON); } - catch(SerializationException E){ - //JSON parsing could fail. Since this an user written part, assume failure is possible (it happens). - System.err.printf("[%s] while loading JSON file for dialog actor. JSON:\n%s\nUsing a default dialog.", E.getMessage(), dialogJSON); - data = json.fromJson(Array.class, DialogData.class, defaultJSON); - } - - for(DialogData dialog:data) { - if(isConditionOk(dialog.condition)) { - loadDialog(dialog); - } - } - } - - private void loadDialog(DialogData dialog) { - setEffects(dialog.effect); - stage.getDialog().getContentTable().clear(); - stage.getDialog().getButtonTable().clear(); - String text = ""; - if(dialog.loctext != null && !dialog.loctext.isEmpty()){ //Check for localized string, otherwise print text. - text = Forge.getLocalizer().getMessage(dialog.loctext); - } else { - text = dialog.text; - } - stage.getDialog().text(text); - if(dialog.options != null) { - for(DialogData option:dialog.options) { - if( isConditionOk(option.condition) ) { - stage.getDialog().getButtonTable().add(Controls.newTextButton(option.name,() -> { - loadDialog(option); - })); - } - } - stage.showDialog(); - } - else { - stage.hideDialog(); - } - } - void setEffects(DialogData.EffectData[] data) { - if(data==null) return; - for(DialogData.EffectData effectData:data) { - Current.player().removeItem(effectData.removeItem); - if(effectData.deleteMapObject<0) - stage.deleteObject(getObjectId()); - else if(effectData.deleteMapObject>0) - stage.deleteObject(effectData.deleteMapObject); - } - } - - boolean isConditionOk(DialogData.ConditionData[] data) { - if(data==null) return true; - for(DialogData.ConditionData condition:data) { - if(condition.item!=null && !condition.item.equals("")) { - if(!Current.player().hasItem(condition.item)) { - return false; - } - } - } - return true; + dialog.activate(); } @Override public void draw(Batch batch, float alpha) { - batch.draw(textureRegion,getX(),getY(),getWidth(),getHeight()); - super.draw(batch,alpha); + batch.draw(textureRegion, getX(), getY(), getWidth(), getHeight()); + super.draw(batch, alpha); } } diff --git a/forge-gui-mobile/src/forge/adventure/character/DummySprite.java b/forge-gui-mobile/src/forge/adventure/character/DummySprite.java new file mode 100644 index 00000000000..42f69931430 --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/character/DummySprite.java @@ -0,0 +1,24 @@ +package forge.adventure.character; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import forge.adventure.stage.MapStage; + +public class DummySprite extends MapActor { + private final TextureRegion textureRegion; + private final MapStage stage; + public DummySprite(int id, TextureRegion textureRegion, MapStage stage) { + super(id); + this.textureRegion = textureRegion; + this.stage = stage; + } + + @Override + public void onPlayerCollide() { stage.resetPosition(); } + + @Override + public void draw(Batch batch, float alpha) { + batch.draw(textureRegion, getX(), getY(), getWidth(), getHeight()); + super.draw(batch, alpha); + } +} diff --git a/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java b/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java index 74c0cb7ed7d..453a1146b74 100644 --- a/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java +++ b/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java @@ -7,6 +7,7 @@ import com.badlogic.gdx.utils.Array; import forge.adventure.data.EnemyData; import forge.adventure.data.RewardData; import forge.adventure.util.Current; +import forge.adventure.util.MapDialog; import forge.adventure.util.Reward; /** @@ -16,6 +17,7 @@ import forge.adventure.util.Reward; public class EnemySprite extends CharacterSprite { EnemyData data; private int id; + public MapDialog dialog; public EnemySprite(EnemyData enemyData) { super(enemyData.sprite); diff --git a/forge-gui-mobile/src/forge/adventure/character/MapActor.java b/forge-gui-mobile/src/forge/adventure/character/MapActor.java index 60092a55964..bbe8e4086ba 100644 --- a/forge-gui-mobile/src/forge/adventure/character/MapActor.java +++ b/forge-gui-mobile/src/forge/adventure/character/MapActor.java @@ -94,6 +94,10 @@ public class MapActor extends Actor { } + public int getId(){ + return objectId; + } + public boolean collideWith(MapActor other) { return collideWith(other.boundingRect()); } diff --git a/forge-gui-mobile/src/forge/adventure/data/DialogData.java b/forge-gui-mobile/src/forge/adventure/data/DialogData.java index 902e5bfe1dc..0a5a429ece4 100644 --- a/forge-gui-mobile/src/forge/adventure/data/DialogData.java +++ b/forge-gui-mobile/src/forge/adventure/data/DialogData.java @@ -9,11 +9,16 @@ public class DialogData { public DialogData[] options; static public class EffectData { - public String removeItem; - public int deleteMapObject; + public String removeItem; //Remove item name from inventory. + public String addItem; //Add item name to inventory. + public int deleteMapObject = 0; //Remove ID from the map. -1 for self. + public int battleWithActorID = 0; //Start a battle with enemy ID. -1 for self if possible. } static public class ConditionData { public String item; + public int flag = 0; + public int actorID = 0; + public boolean not = false; } } diff --git a/forge-gui-mobile/src/forge/adventure/stage/MapStage.java b/forge-gui-mobile/src/forge/adventure/stage/MapStage.java index 794b099d1fe..7ec1259092b 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/MapStage.java +++ b/forge-gui-mobile/src/forge/adventure/stage/MapStage.java @@ -26,10 +26,7 @@ import forge.adventure.pointofintrest.PointOfInterestChanges; import forge.adventure.scene.DuelScene; import forge.adventure.scene.RewardScene; import forge.adventure.scene.SceneType; -import forge.adventure.util.Config; -import forge.adventure.util.Controls; -import forge.adventure.util.Current; -import forge.adventure.util.Reward; +import forge.adventure.util.*; import forge.adventure.world.WorldSave; import forge.screens.TransitionScreen; import forge.sound.SoundEffectType; @@ -190,6 +187,7 @@ public class MapStage extends GameStage { text += E.getDescription(); dialog.text(text); dialog.getButtonTable().add(Controls.newTextButton("OK", () -> { this.hideDialog(); })); + dialog.setKeepWithinStage(true); showDialog(); } @@ -286,6 +284,14 @@ public class MapStage extends GameStage { case "enemy": EnemySprite mob = new EnemySprite(id, WorldData.getEnemy(prop.get("enemy").toString())); addMapActor(obj, mob); + if(prop.get("dialog") != null && !prop.get("dialog").toString().isEmpty()) { + mob.dialog = new MapDialog(prop.get("dialog").toString(), this, mob.getId()); + } + break; + case "dummy": //Does nothing. Mostly obstacles to be removed by ID by switches or such. + TiledMapTileMapObject obj2 = (TiledMapTileMapObject) obj; + DummySprite D = new DummySprite(id, obj2.getTextureRegion(), this); + addMapActor(obj, D); break; case "inn": addMapActor(obj, new OnCollide(new Runnable() { @@ -305,7 +311,6 @@ public class MapStage extends GameStage { break; case "dialog": if(obj instanceof TiledMapTileMapObject) { - //Sanity check TiledMapTileMapObject tiledObj = (TiledMapTileMapObject) obj; DialogActor dialog = new DialogActor(this, id, prop.get("dialog").toString(), tiledObj.getTextureRegion()); addMapActor(obj, dialog); @@ -353,6 +358,7 @@ public class MapStage extends GameStage { public boolean exit() { isLoadingMatch = false; + effect = null; //Reset dungeon effects. clearIsInMap(); Forge.switchScene(SceneType.GameScene.instance); return true; @@ -399,7 +405,22 @@ public class MapStage extends GameStage { } } return false; + } + public boolean lookForID(int id){ + for(MapActor A : new Array.ArrayIterator<>(actors)){ + if(A.getId() == id) + return true; + } + return false; + } + + public EnemySprite getEnemyByID(int id) { + for(MapActor A : new Array.ArrayIterator<>(actors)){ + if(A instanceof EnemySprite && ((EnemySprite) A).getId() == id) + return ((EnemySprite) A); + } + return null; } protected void getReward() { @@ -423,41 +444,13 @@ public class MapStage extends GameStage { if (actor instanceof EnemySprite) { EnemySprite mob = (EnemySprite) actor; currentMob = mob; - if (mob.getData().deck == null || mob.getData().deck.isEmpty()) { - currentMob.setAnimation(CharacterSprite.AnimationTypes.Death); - Gdx.input.vibrate(50); - startPause(0.3f, new Runnable() { - @Override - public void run() { - MapStage.this.getReward(); - } - }); + if (mob.dialog != null){ //This enemy has something to say. Display a dialog like if it was a DialogActor. + resetPosition(); + showDialog(); + mob.dialog.activate(); break; - } else { - player.setAnimation(CharacterSprite.AnimationTypes.Attack); - mob.setAnimation(CharacterSprite.AnimationTypes.Attack); - Gdx.input.vibrate(50); - Forge.setCursor(null, Forge.magnifyToggle ? "1" : "2"); - SoundSystem.instance.play(SoundEffectType.ManaBurn, false); - if (!isLoadingMatch) { - isLoadingMatch = true; - Forge.setTransitionScreen(new TransitionScreen(new Runnable() { - @Override - public void run() { - Forge.clearTransitionScreen(); - } - }, ScreenUtils.getFrameBufferTexture(), true, false)); - } - startPause(0.4f, new Runnable() { - @Override - public void run() { - DuelScene S = ((DuelScene) SceneType.DuelScene.instance); - S.setEnemy(mob); - S.setPlayer(player); - S.setDungeonEffect(effect); - Forge.switchScene(SceneType.DuelScene.instance); - } - }); + } else { //Duel the enemy. + beginDuel(mob); break; } } else if (actor instanceof RewardSprite) { @@ -479,6 +472,34 @@ public class MapStage extends GameStage { } } + public void beginDuel(EnemySprite mob){ + if(mob == null) return; + currentMob = mob; + player.setAnimation(CharacterSprite.AnimationTypes.Attack); + mob.setAnimation(CharacterSprite.AnimationTypes.Attack); + Gdx.input.vibrate(50); + Forge.setCursor(null, Forge.magnifyToggle ? "1" : "2"); + SoundSystem.instance.play(SoundEffectType.ManaBurn, false); + if (!isLoadingMatch) { + isLoadingMatch = true; + Forge.setTransitionScreen(new TransitionScreen(new Runnable() { + @Override + public void run() { + Forge.clearTransitionScreen(); + } + }, ScreenUtils.getFrameBufferTexture(), true, false)); + } + startPause(0.4f, new Runnable() { + @Override + public void run() { + DuelScene S = ((DuelScene) SceneType.DuelScene.instance); + S.setEnemy(mob); + S.setPlayer(player); + S.setDungeonEffect(effect); + Forge.switchScene(SceneType.DuelScene.instance); + } + }); + } public void setPointOfInterest(PointOfInterestChanges change) { changes = change; @@ -488,8 +509,6 @@ public class MapStage extends GameStage { return isInMap; } - - public void showDialog() { dialog.show(dialogStage); dialogOnlyInput=true; diff --git a/forge-gui-mobile/src/forge/adventure/util/MapDialog.java b/forge-gui-mobile/src/forge/adventure/util/MapDialog.java new file mode 100644 index 00000000000..dba55f3f3cf --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/util/MapDialog.java @@ -0,0 +1,125 @@ +package forge.adventure.util; + +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.SerializationException; +import forge.Forge; +import forge.adventure.data.DialogData; +import forge.adventure.stage.MapStage; + +public class MapDialog { + private final MapStage stage; + private Array data; + private final int parentID; + + static private final String defaultJSON = "[\n" + + " {\n" + + " \"effect\":[],\n" + + " \"name\":\"Error\",\n" + + " \"text\":\"This is a fallback dialog.\\nPlease check Forge logs for errors.\",\n" + + " \"condition\":[],\n" + + " \"options\":[\n" + + " { \"name\":\"OK\" }\n" + + " ]\n" + + " }\n" + + "]"; + + + public MapDialog(String S, MapStage ST, int parentID) { + this.stage = ST; + this.parentID = parentID; + Json json = new Json(); + if (S.isEmpty()){ + System.err.print("Dialog error. Dialog property is empty.\n"); + this.data = json.fromJson(Array.class, DialogData.class, defaultJSON); + return; + } + try { data = json.fromJson(Array.class, DialogData.class, S); } + catch(SerializationException E){ + //JSON parsing could fail. Since this an user written part, assume failure is possible (it happens). + System.err.printf("[%s] while loading JSON file for dialog actor. JSON:\n%s\nUsing a default dialog.", E.getMessage(), S); + this.data = json.fromJson(Array.class, DialogData.class, defaultJSON); + } + } + + private void loadDialog(DialogData dialog) { + setEffects(dialog.effect); + stage.getDialog().getContentTable().clear(); + stage.getDialog().getButtonTable().clear(); + String text; + if(dialog.loctext != null && !dialog.loctext.isEmpty()){ //Check for localized string, otherwise print text. + text = Forge.getLocalizer().getMessage(dialog.loctext); + } else { + text = dialog.text; + } + + int charCount = 0; + + stage.getDialog().text(text); + if(dialog.options != null) { + for(DialogData option:dialog.options) { + if( isConditionOk(option.condition) ) { + charCount += option.name.length(); + if(charCount > 35){ //Gross hack. + stage.getDialog().getButtonTable().row(); + charCount = 0; + } + stage.getDialog().getButtonTable().add(Controls.newTextButton(option.name,() -> loadDialog(option))); + + } + } + stage.showDialog(); + } + else { + stage.hideDialog(); + } + } + + public void activate() { + for(DialogData dialog:data) { + if(isConditionOk(dialog.condition)) { + loadDialog(dialog); + } + } + } + + void setEffects(DialogData.EffectData[] data) { + if(data==null) return; + for(DialogData.EffectData E:data) { + if (E.removeItem != null){ + Current.player().removeItem(E.removeItem); + } + if (E.addItem != null){ + Current.player().addItem(E.addItem); + } + if (E.deleteMapObject != 0){ + if(E.deleteMapObject < 0) stage.deleteObject(parentID); + else stage.deleteObject(E.deleteMapObject); + } + if (E.battleWithActorID != 0){ + if(E.battleWithActorID < 0) stage.beginDuel(stage.getEnemyByID(parentID)); + else stage.beginDuel(stage.getEnemyByID(E.battleWithActorID)); + } + + + + } + } + + boolean isConditionOk(DialogData.ConditionData[] data) { + if(data==null) return true; + for(DialogData.ConditionData condition:data) { + if(condition.item != null && !condition.item.isEmpty()) { //Check for item. + if(Current.player().hasItem(condition.item)) { + return ((condition.not) ? false : true); + } + } + if(condition.actorID != 0) { //Check for actor ID. + boolean result = stage.lookForID(condition.actorID); + if(condition.not) result = !result; + return result; + } + } + return true; + } +} diff --git a/forge-gui/res/adventure/Shandalar/maps/map/debug_map.tmx b/forge-gui/res/adventure/Shandalar/maps/map/debug_map.tmx index 039013d6ec1..bdf8c9d7f65 100644 --- a/forge-gui/res/adventure/Shandalar/maps/map/debug_map.tmx +++ b/forge-gui/res/adventure/Shandalar/maps/map/debug_map.tmx @@ -1,5 +1,5 @@ - + @@ -20,7 +20,7 @@ - eJyTEGBgkBjFGFgaiLdhwVI0tlcSRAthYvFRe6mKa/kZGKqAuJofYl8llF/DT1t70f1Na3/S015pKI2eZ0Bi26H2bsUiT2nekoTS6ACbGDZAbliQam+jJANDkyR17IWFGQyg85HBXKCd86hkLyVg1N5Re0ftHbV3sNirREB+1F7iMKwthYzx2YuulpptLnz2UssOQnYj20trO5HjHmQvpe06ANvAfIE= + eJyTEGBgkBjFGFgaiLdhwVI0tlcSRAthYvFRe6mKa/kZGKqAuJofYl8llF/DT1t70f1Na3/S015pKI2eZ0Bi26H2bsUiT2nekoTS6ACbGDZAbliQam+jJANDkyR17IWFGQyg85HBXKCd86hkLyVg1N5RewfaXuQyiZ72UhOQY+9IiV9S7FUiID9qL3EY1pZCxvjsRVdLzTYXPnupZQchu5HtpbWdyHEPspfSdh0A9Qp8zA== @@ -72,9 +72,9 @@ - + - + @@ -106,8 +106,62 @@ + [ + { + "effect":[], + "name":"ABC", + "text":"I am an elf. I do elf things like hugging trees and being pretty.", + "loctext":"", + "condition":[], + "options":[ + { "name":"OK" }, + { + "name":"Fight me, elf!", + "text": "Gladly.", + "options": [ { "name": "I FEAR NOTHING!!?", "effect": [ { "battleWithActorID": -1 } ]} ] + }, + { + "name":"I wanna fight Emrakul over there!", + "condition": [ { "actorID": 50 } ], + "text": "Really? Oh well... your funeral.\nHEY EMMY! THIS DUDE WANTS TO DANCE!", + "options": [ { "name": "WAIT IT WAS A JOKE!!!", "effect": [ { "battleWithActorID": 50 } ]} ] + }, + { + "name":"I wanna fight Emrakul over there!", + "condition": [ { "actorID": 50, "not": true } ], + "text": "She left. Crying. You monster.", + "options": [ { "name": "Sorry..." } ] + }, + { + "name":"That's cool dude.", + "condition": [ { "item": "Treasure", "not": true } ], + "effect": [ { "addItem": "Treasure" } ], + "text": "You get it. Take this.", + "options": [ { "name": "Thanks bro." } ] + }, + { + "name":"Can you open that hidden wall?", + "condition": [ { "actorID": 83 } ], + "text": "Since you asked nicely, I shall.", + "options": [ { "name": "Thanks bro.", "effect": [ { "deleteMapObject": 83 } ]} ] + } + ] + } +] + + + + + + + + + + + +