Skip to content

Commit 567209b

Browse files
Merge pull request #206 from UQcsse3200/book
Improve Book
2 parents f4c8dbe + 1e1da61 commit 567209b

File tree

14 files changed

+291
-41
lines changed

14 files changed

+291
-41
lines changed

source/core/assets/configs/tower.json

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"boneTower": {
3-
"name": "bone tower",
3+
"name": "Bone",
44
"lore": "Crafted by ancient tribes, Bone Towers channel the primal energy of the land. Their flying bone projectiles carry the spirits of long-dead guardians, sending a chilling warning to any who dare approach.",
55
"base": {
66
"level_A": 1,
@@ -18,7 +18,7 @@
1818
}
1919
},
2020
"dinoTower": {
21-
"name": "dino tower",
21+
"name": "Dino",
2222
"locked": true,
2323
"lore": "Forged from the bones and scales of ancient beasts, Dino Towers launch fireballs that scorch everything in their path. Their ferocious roars echo across the battlefield, intimidating foes before they even reach your defenses.",
2424
"base": {
@@ -37,7 +37,7 @@
3737
}
3838
},
3939
"cavemenTower": {
40-
"name": "cavemen tower",
40+
"name": "Cavemen",
4141
"lore": "Cavemen Towers are staffed by expert marksmen armed with guns salvaged from defeated AI bots. With sharp eyes and steady hands, they make sure nothing slips through their sights.",
4242
"base": {
4343
"level_A": 1,
@@ -55,6 +55,8 @@
5555
}
5656
},
5757
"pterodactylTower": {
58+
"name": "Pterodactyl",
59+
"lore": "Hatched from a relic nest of ancient beasts, the Pterodactyl Tower binds its fierce instincts to modern defense systems. Its wings never tire, and its piercing shrieks echo across the battlefield as it defends its territory with primal fury.",
5860
"base": {
5961
"level_A": 1,
6062
"level_B": 1,
@@ -71,6 +73,8 @@
7173
}
7274
},
7375
"supercavemenTower": {
76+
"name": "Super Cavemen",
77+
"lore": "Super Cavemen are the result of experimental enhancements on elite hunters. With eyes that burn with laser fury and muscles honed to perfection, they strike fear into all who dare approach their territory.",
7478
"base": {
7579
"level_A": 1,
7680
"level_B": 1,
@@ -87,6 +91,8 @@
8791
}
8892
},
8993
"totemTower": {
94+
"name": "Totem",
95+
"lore": "Totem Towers are relics of a forgotten civilization, imbued with ancient magic that resonates with nearby machinery. Their mystical aura inspires and empowers all who stand within its influence.",
9096
"base": {
9197
"level_A": 1,
9298
"level_B": 1,
@@ -103,6 +109,8 @@
103109
}
104110
},
105111
"bankTower": {
112+
"name": "Bank",
113+
"lore": "Bank Towers are fortified vaults staffed by clever financiers. Their mechanisms mint currency from the battlefield itself, ensuring that wealth flows steadily to fund the defense of the realm.",
106114
"base": {
107115
"level_A": 1,
108116
"level_B": 1,
@@ -119,6 +127,8 @@
119127
}
120128
},
121129
"raftTower": {
130+
"name": "Raft",
131+
"lore": "Raft Towers are crewed by fearless Viking archers who mastered both sea and bow. Their handmade ships drift silently across the water, striking down enemies before they can ever reach the shore.",
122132
"base": {
123133
"level_A": 1,
124134
"level_B": 1,
@@ -135,6 +145,8 @@
135145
}
136146
},
137147
"frozenmamoothskullTower": {
148+
"name": "Frozen Mammoth Skull",
149+
"lore": "The Frozen Mammoth Skull Tower channels the lingering spirit of a long-extinct titan. Its frosty breath echoes through the ages, freezing all who dare tread near its glacial domain.",
138150
"base": {
139151
"level_A": 1,
140152
"level_B": 1,
@@ -151,6 +163,8 @@
151163
}
152164
},
153165
"bouldercatapultTower": {
166+
"name": "Boulder Catapult",
167+
"lore": "Constructed under the guidance of ancient village shamans, the Boulder Catapult Tower channels raw strength and earth’s fury. Each stone it launches carries the weight of the mountains themselves, crushing all who stand in its way.",
154168
"base": {
155169
"level_A": 1,
156170
"level_B": 1,
@@ -167,6 +181,9 @@
167181
}
168182
},
169183
"villageshamanTower": {
184+
"name": "Village Shaman",
185+
"lore": "The Village Shaman Tower draws its strength from ancient spirits and forgotten rituals. Guided by wisdom older than time, its chants summon lightning and energy from the skies, striking down enemies with divine precision.",
186+
"locked": true,
170187
"base": {
171188
"level_A": 1,
172189
"level_B": 1,
2.15 MB
Loading
-620 KB
Loading

source/core/src/main/com/csse3200/game/GdxGame.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,16 @@ private Screen newScreen(ScreenType screenType, boolean isContinue, String saveF
161161
return new BookScreen(this, BookPage.ENEMY_PAGE);
162162
case TOWER_BOOK:
163163
return new BookScreen(this, BookPage.TOWER_PAGE);
164+
case HERO_BOOK:
165+
return new BookScreen(this, BookPage.HERO_PAGE);
164166
default:
165167
return null;
166168
}
167169
}
168170

169171
public enum ScreenType {
170172
MAIN_MENU, MAIN_GAME, SETTINGS, SAVE_SELECTION, OPENING_CUTSCENE, VICTORY,
171-
MAP_SELECTION, BOOK, CURRENCY_BOOK, ENEMY_BOOK, TOWER_BOOK, UPGRADES
173+
MAP_SELECTION, BOOK, CURRENCY_BOOK, ENEMY_BOOK, TOWER_BOOK, UPGRADES, HERO_BOOK
172174
}
173175

174176
/**

source/core/src/main/com/csse3200/game/components/book/BookComponent.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
package com.csse3200.game.components.book;
22

3-
import com.csse3200.game.components.deck.CurrencyBookDeckComponent;
4-
import com.csse3200.game.components.deck.DeckComponent;
5-
import com.csse3200.game.components.deck.EnemyBookDeckComponent;
6-
import com.csse3200.game.components.deck.TowerBookDeckComponent;
7-
import com.csse3200.game.entities.configs.CurrencyConfig;
8-
import com.csse3200.game.entities.configs.EnemyConfig;
9-
import com.csse3200.game.entities.configs.TowerConfig;
3+
import com.csse3200.game.components.deck.*;
4+
import com.csse3200.game.entities.configs.*;
105
import com.csse3200.game.files.FileLoader;
116
import org.slf4j.Logger;
127
import org.slf4j.LoggerFactory;
@@ -181,4 +176,20 @@ private static List<DeckComponent> createDecks() {
181176
return decks;
182177
}
183178
}
179+
180+
public static class HeroBookComponent extends BookComponent {
181+
public HeroBookComponent() {
182+
super("HEROES", createDecks());
183+
}
184+
185+
private static List<DeckComponent> createDecks() {
186+
List<DeckComponent> decks = new ArrayList<>();
187+
188+
decks.add(HeroBookDeckComponent.from("Hero", new HeroConfig()));
189+
decks.add(HeroBookDeckComponent.from("Engineer", new EngineerConfig()));
190+
decks.add(HeroBookDeckComponent.from("Samurai", new SamuraiConfig()));
191+
192+
return decks;
193+
}
194+
}
184195
}

source/core/src/main/com/csse3200/game/components/book/BookDisplay.java

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.badlogic.gdx.audio.Sound;
44
import com.badlogic.gdx.graphics.Color;
5+
import com.badlogic.gdx.graphics.Pixmap;
56
import com.badlogic.gdx.graphics.Texture;
67
import com.badlogic.gdx.graphics.g2d.NinePatch;
78
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
@@ -50,6 +51,7 @@ public class BookDisplay extends UIComponent {
5051

5152
private DeckComponent deck;
5253
private boolean hasJustOpenedBook = true;
54+
private static final Color borderColor = new Color(0.38f, 0.26f, 0.04f, 1f);
5355

5456
private Table contentListTable;
5557
private Table titleTable;
@@ -71,6 +73,9 @@ public BookDisplay(BookPage bookPage) {
7173
} else if (bookPage == BookPage.TOWER_PAGE) {
7274
this.book = new BookComponent.TowerBookComponent();
7375
maxWordsLore = 25;
76+
} else if (bookPage == BookPage.HERO_PAGE) {
77+
this.book = new BookComponent.HeroBookComponent();
78+
maxWordsLore = 15;
7479
}
7580
this.decks = book == null ? null : book.getDecks();
7681
}
@@ -171,8 +176,18 @@ private void renderContentList() {
171176
Table table = new Table();
172177
table.setFillParent(true);
173178

179+
int imagesPerRow = 4;
180+
float padLeftFactor = 0.16f;
181+
if (decks.size() <= 8) {
182+
imagesPerRow = 2;
183+
padLeftFactor = 0.2f;
184+
} else if (decks.size() <= 15) {
185+
imagesPerRow = 3;
186+
padLeftFactor = 0.18f;
187+
}
188+
174189
// Left content list
175-
table.top().left().padLeft(stageWidth * 0.2f).padTop(stageHeight * 0.2f);
190+
table.top().left().padLeft(stageWidth * padLeftFactor).padTop(stageHeight * 0.2f);
176191

177192
// Book title
178193
Label labelTitle = new Label(this.book.getTitle(), skin, "large");
@@ -184,11 +199,10 @@ private void renderContentList() {
184199
.padTop(stageHeight * 0.12f);
185200
titleTable.add(labelTitle);
186201

202+
187203
// Scale buttons relative to screen width
188-
float buttonW = stageWidth * 0.12f;
189-
float buttonH = stageHeight * 0.12f;
204+
float buttonSize = stageWidth * 0.20f / imagesPerRow;
190205

191-
int imagesPerRow = 2;
192206
int count = 0;
193207

194208
for (DeckComponent currentDeck : decks) {
@@ -217,13 +231,68 @@ public void changed(ChangeEvent changeEvent, Actor actor) {
217231
}
218232
});
219233
}
220-
table.add(button).size(buttonW, buttonH).padRight(stageWidth * 0.01f);
234+
235+
// Wrap button with a bordered table
236+
Table borderedButton = new Table();
237+
borderedButton.setBackground(new TextureRegionDrawable(
238+
new TextureRegion(createBorderTexture(borderColor, 2))
239+
));
240+
borderedButton.add(button).size(buttonSize, buttonSize).pad(2);
241+
242+
table.add(borderedButton).padRight(stageWidth * 0.08f / imagesPerRow);
221243
}
222244

223245
stage.addActor(titleTable);
224246
stage.addActor(table);
225247
}
226248

249+
private Texture createBorderTexture(Color color, int thickness) {
250+
int size = 64;
251+
Pixmap pixmap = new Pixmap(size, size, Pixmap.Format.RGBA8888);
252+
253+
// Make everything transparent first
254+
pixmap.setColor(0, 0, 0, 0);
255+
pixmap.fill();
256+
257+
// Set border color
258+
pixmap.setColor(color);
259+
260+
// Draw multiple nested rectangles to create the desired thickness
261+
for (int i = 0; i < thickness; i++) {
262+
pixmap.drawRectangle(i, i, size - 2 * i, size - 2 * i);
263+
}
264+
265+
Texture texture = new Texture(pixmap);
266+
pixmap.dispose();
267+
return texture;
268+
}
269+
270+
// Create a NinePatchDrawable that is a border (transparent center, colored frame)
271+
private NinePatchDrawable createBorderNinePatchDrawable(int size, int thickness, Color color) {
272+
Pixmap pixmap = new Pixmap(size, size, Pixmap.Format.RGBA8888);
273+
// Fill fully transparent first
274+
pixmap.setColor(0, 0, 0, 0);
275+
pixmap.fill();
276+
277+
// Draw border by drawing rectangles of 1px thickness inwards
278+
pixmap.setColor(color);
279+
for (int i = 0; i < thickness; i++) {
280+
pixmap.drawRectangle(i, i, size - 2 * i, size - 2 * i);
281+
}
282+
283+
Texture texture = new Texture(pixmap);
284+
pixmap.dispose();
285+
286+
// Create NinePatch so the center area stretches but border remains intact
287+
NinePatch patch = new NinePatch(new TextureRegion(texture),
288+
thickness, thickness, thickness, thickness);
289+
NinePatchDrawable drawable = new NinePatchDrawable(patch);
290+
291+
// NOTE: texture will be owned by the patch; dispose later when screen is disposed
292+
return drawable;
293+
}
294+
295+
227296
/**
228297
* Renders the details of a selected deck on the right-hand panel.
229298
*
@@ -251,23 +320,37 @@ private void renderRightDeck(DeckComponent deck) {
251320
// Clear the whole right panel before re-rendering
252321
rightTable.clear();
253322

254-
// --- IMAGE ---
323+
// --- IMAGE --- (inside renderRightDeck)
255324
Texture tex = ServiceLocator.getResourceService()
256325
.getAsset(stats.get(DeckComponent.StatType.TEXTURE_PATH), Texture.class);
257326
Image rightImage = new Image(new TextureRegionDrawable(new TextureRegion(tex)));
258327

259328
float imageW = stageWidth * 0.15f;
260329
float imageH = stageHeight * 0.25f;
261-
rightTable.add(rightImage).size(imageW, imageH).center();
330+
331+
// Create border drawable (size 64, thickness 2 px)
332+
int borderSize = 64;
333+
int borderThicknessPx = 6;
334+
NinePatchDrawable borderDrawable = createBorderNinePatchDrawable(borderSize, borderThicknessPx, borderColor);
335+
336+
// Wrap the image in a Table so the background is the border drawable
337+
Table borderedImage = new Table();
338+
borderedImage.setBackground(borderDrawable);
339+
340+
// Add the image inside the bordered table with a small padding so it doesn't overlap the border
341+
float pad = borderThicknessPx; // Scale this using stageWidth/stageHeight if needed
342+
borderedImage.add(rightImage).size(imageW - pad * 2, imageH - pad * 2).center();
343+
rightTable.add(borderedImage).size(imageW, imageH).center();
262344
rightTable.row().padTop(stageHeight * 0.01f);
263345

346+
264347
// --- NAME ---
265348
String name = stats.get(DeckComponent.StatType.NAME);
266349
if (name == null || name.isEmpty()) {
267350
name = "Unknown";
268351
}
269352
Label nameLabel = new Label(name, skin, "large");
270-
nameLabel.setFontScale(stageWidth * 0.001f);
353+
nameLabel.setFontScale(stageWidth * 0.0008f);
271354
rightTable.add(nameLabel).center();
272355
rightTable.row().padTop(stageHeight * 0.01f);
273356

@@ -276,7 +359,7 @@ private void renderRightDeck(DeckComponent deck) {
276359
if (lore != null && !lore.isEmpty()) {
277360
String trimmedLore = trimWords(lore, maxWordsLore);
278361
Label loreLabel = new Label(trimmedLore, skin, "small"); // use a smaller style
279-
loreLabel.setFontScale(stageWidth * 0.001f);
362+
loreLabel.setFontScale(stageWidth * 0.0008f);
280363
loreLabel.setWrap(true);
281364
rightTable.add(loreLabel)
282365
.width(stageWidth * 0.3f)

source/core/src/main/com/csse3200/game/components/book/BookPage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ public enum BookPage {
88
TOWER_PAGE,
99
ENEMY_PAGE,
1010
CURRENCY_PAGE,
11+
HERO_PAGE,
1112
NONE
1213
}

0 commit comments

Comments
 (0)