diff --git a/public/assets/OOP Bow.svg b/public/assets/OOP Bow.svg
new file mode 100644
index 0000000..5749ef9
--- /dev/null
+++ b/public/assets/OOP Bow.svg
@@ -0,0 +1,193 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Chest.svg b/public/assets/OOP Chest.svg
new file mode 100644
index 0000000..afd805d
--- /dev/null
+++ b/public/assets/OOP Chest.svg
@@ -0,0 +1,403 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Daniel.svg b/public/assets/OOP Daniel.svg
new file mode 100644
index 0000000..0b2824e
--- /dev/null
+++ b/public/assets/OOP Daniel.svg
@@ -0,0 +1,785 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Dragon.svg b/public/assets/OOP Dragon.svg
new file mode 100644
index 0000000..c933172
--- /dev/null
+++ b/public/assets/OOP Dragon.svg
@@ -0,0 +1,586 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP FireGloves.svg b/public/assets/OOP FireGloves.svg
new file mode 100644
index 0000000..fce31df
--- /dev/null
+++ b/public/assets/OOP FireGloves.svg
@@ -0,0 +1,505 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Greg.svg b/public/assets/OOP Greg.svg
new file mode 100644
index 0000000..1291f0e
--- /dev/null
+++ b/public/assets/OOP Greg.svg
@@ -0,0 +1,785 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP HealingPotion.svg b/public/assets/OOP HealingPotion.svg
new file mode 100644
index 0000000..6af6b7d
--- /dev/null
+++ b/public/assets/OOP HealingPotion.svg
@@ -0,0 +1,361 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP HealingWand.svg b/public/assets/OOP HealingWand.svg
new file mode 100644
index 0000000..2269fdc
--- /dev/null
+++ b/public/assets/OOP HealingWand.svg
@@ -0,0 +1,159 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Justin.svg b/public/assets/OOP Justin.svg
new file mode 100644
index 0000000..cd8680e
--- /dev/null
+++ b/public/assets/OOP Justin.svg
@@ -0,0 +1,785 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Longsword.svg b/public/assets/OOP Longsword.svg
new file mode 100644
index 0000000..c4d61a4
--- /dev/null
+++ b/public/assets/OOP Longsword.svg
@@ -0,0 +1,255 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP Nicho.svg b/public/assets/OOP Nicho.svg
new file mode 100644
index 0000000..9f28d25
--- /dev/null
+++ b/public/assets/OOP Nicho.svg
@@ -0,0 +1,785 @@
+
+
\ No newline at end of file
diff --git a/public/assets/OOP UselessAmulet.svg b/public/assets/OOP UselessAmulet.svg
new file mode 100644
index 0000000..578094c
--- /dev/null
+++ b/public/assets/OOP UselessAmulet.svg
@@ -0,0 +1,161 @@
+
+
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 7531b07..25e3765 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -25,10 +25,10 @@ export interface AppState {
function getNewGameState(): AppState{
return {
characters: [
- new Warrior('Conan', 1),
- new Cleric('Cuthbert', 2),
- new Mage('Merlin', 3),
- new Thief('Bilbo', 4)
+ new Warrior('Greg', 1),
+ new Cleric('Justin', 2),
+ new Mage('Nicho', 3),
+ new Thief('Daniel', 4)
],
chests: GetItemsInTreasureChests().map(x => {return {item: x, opened: false}}),
dragonHP: 100,
diff --git a/src/off-limits/ChestText.tsx b/src/off-limits/ChestText.tsx
index d9e487d..6fbe8f2 100644
--- a/src/off-limits/ChestText.tsx
+++ b/src/off-limits/ChestText.tsx
@@ -2,12 +2,8 @@ import React from "react";
export const ChestText: React.FC = () => {
return (
-
- {' ______ ___\n' +
- '// // \\\\\n' +
- '#=====##===#\n' +
- '| [8] || |\n' +
- '|_____||___|\n'+'\n'}
-
+
+

+
);
};
\ No newline at end of file
diff --git a/src/off-limits/CombatUI.tsx b/src/off-limits/CombatUI.tsx
index d654910..6e1b698 100644
--- a/src/off-limits/CombatUI.tsx
+++ b/src/off-limits/CombatUI.tsx
@@ -10,7 +10,7 @@ export const CombatUI: React.FC<{
return (
-
+
| 50ft |
@@ -82,10 +82,11 @@ export const CharacterCombatUI: React.FC<{
}> = (props) => {
const isDead = props.character.health <= 0;
const hpClass = props.character.health <= 1 ? 'red' : props.character.health < 5 ? 'yellow': ''
+ const playerImage = `/assets/OOP ${props.character.getASCIIStatus()}.svg`;
return (
-
- {props.character.getASCIIStatus()}
+
+
[{props.character?.health}]
diff --git a/src/off-limits/Dragon.tsx b/src/off-limits/Dragon.tsx
index 97b2403..03bae58 100644
--- a/src/off-limits/Dragon.tsx
+++ b/src/off-limits/Dragon.tsx
@@ -14,21 +14,7 @@ export const Dragon: React.FC<{
BOSS DRAGON
{hpString}
- {' _/=======() \n' +
- '(/___ /|\\ ()==========__\n' +
- ' _/ | \\ //| ______/ )\n' +
- ' _| \\ // | _/\n' +
- ' |/|_ // //\n' +
- ' (oo) _// /\n' +
- ' //_/_/ / |\n' +
- ' @@/ |= |\n' +
- ' _=_ |\n' +
- ' == |_ snd\n' +
- ' __(===( )\\\n' +
- ' (((~) __(_/ |\n' +
- ' (((~) /\n' +
- ' ______/ /\n' +
- 'Art by Shanaka Dias'}
+
);
};
diff --git a/src/off-limits/LootUI.tsx b/src/off-limits/LootUI.tsx
index 31499e2..2361c3d 100644
--- a/src/off-limits/LootUI.tsx
+++ b/src/off-limits/LootUI.tsx
@@ -38,14 +38,23 @@ export const LootUI: React.FC<{
{x.name} the {x.classname()} finds
{
- props.state.chests[i].opened ?
- {props.state.chests[i].item.name}
- | :
-
-
- |
+ props.state.chests[i].opened ?
+
+
+
+
+ {props.state.chests[i].item.name}
+
+ |
+ :
+
+
+
+ |
}
))}
diff --git a/src/style.css b/src/style.css
index d7e3479..d704bea 100644
--- a/src/style.css
+++ b/src/style.css
@@ -12,7 +12,7 @@ body {
white-space: pre;
}
.dragon {
- width: 49%;
+ width: 30%;
display: inline-block;
}
.hp {
@@ -21,16 +21,12 @@ body {
.game {
display: flex;
}
-.left {
- display: inline-block;
- width: 49%;
-}
-table.grid {
+table.grid, table.combat-grid {
font-family: monospace;
font-size: 1em;
margin: auto;
}
-.grid td {
+.grid td, .combat-grid td {
border-collapse: collapse;
text-align: center;
width: 3em;
@@ -39,6 +35,10 @@ table.grid {
padding: 0;
border: 1px solid darkgreen;
}
+.combat-grid tbody td {
+ width: 128px;
+ height: 128px;
+}
.block {
display: block;
}
@@ -98,4 +98,18 @@ td.loot-chest{
}
.yellow{
color: yellow;
+}
+
+.size-128px {
+ height: 128px;
+ width: 128px;
+}
+
+.size-dragon {
+ height: 400px;
+ width: 400px;
+}
+
+.display-block {
+ display: block;
}
\ No newline at end of file
diff --git a/src/workshop/BaseCharacter.tsx b/src/workshop/BaseCharacter.tsx
index e79b5f2..3bc5f42 100644
--- a/src/workshop/BaseCharacter.tsx
+++ b/src/workshop/BaseCharacter.tsx
@@ -1,24 +1,51 @@
import { CharacterClassName, ICharacter, ICharacterActionDecision } from "../off-limits/ICharacter";
-import { IWeapon, IItem } from "../off-limits/IWeapons";
+import { IWeapon, IItem, isMeleeWeapon, IConsumableItem, IEnchantedItem, IMeleeWeapon } from "../off-limits/IWeapons";
+import { isWeaponFitForUse as isWeaponFit } from "./Weapons";
-//todo: use this base class somehow in Characters.tsx
-export class Character implements ICharacter {
- name: string = '';
- health: number = 5;
+export class BaseCharacter implements ICharacter {
+ health: number = 10;
position: number = 10;
weapons: IWeapon[] = [];
item?: IItem;
-
+ feet = new Feet(this);
+
+ constructor(
+ public name: string,
+ public key : number,
+ private className : CharacterClassName,
+ private ASCIIStatus : string) {
+ }
+
classname(): CharacterClassName {
- throw new Error("Method not implemented.");
+ return this.className;
}
+
move(){
- throw new Error("Method not implemented.");
+ this.feet.move();
}
- chooseAction(): ICharacterActionDecision{
- throw new Error("Method not implemented.");
+
+ chooseAction(): ICharacterActionDecision {
+ if (this.health < 3 && this.item && (this.item as IConsumableItem).healthBonus)
+ return { use: this.item };
+ if (this.health < 5 && this.item && (this.item as IEnchantedItem).partyHealthBonus)
+ return { use: this.item };
+
+ let availableWeapons = this.weapons.filter(weapon => isWeaponFit(weapon));
+
+ return { attack: availableWeapons[0] };
}
+
getASCIIStatus(): string {
- throw new Error("Method not implemented.");
+ return this.ASCIIStatus;
+ }
+}
+
+export class Feet {
+ constructor(private character: ICharacter) { }
+ move() {
+ if (this.character.weapons.length &&
+ isMeleeWeapon(this.character.weapons.filter(weapon => isWeaponFit(weapon))[0])) {
+ this.character.position = Math.max(this.character.position - 5, 1);
}
+ }
}
\ No newline at end of file
diff --git a/src/workshop/Characters.tsx b/src/workshop/Characters.tsx
index 16ba4f4..9807e0e 100644
--- a/src/workshop/Characters.tsx
+++ b/src/workshop/Characters.tsx
@@ -1,123 +1,45 @@
-import { ICharacter, CharacterClassName, equip, ICharacterActionDecision } from '../off-limits/ICharacter';
-import { IWeapon, IItem, isMeleeWeapon } from '../off-limits/IWeapons';
-import { Character } from './BaseCharacter';
+import { equip, ICharacterActionDecision } from '../off-limits/ICharacter';
+import { IEnchantedItem } from '../off-limits/IWeapons';
+import { BaseCharacter } from './BaseCharacter';
import {
ClericStartItem,
MageStartItem,
+ ThieAditionalfStartItem as ThiefAditionalStartItem,
ThiefStartItem,
WarriorStartItem,
} from './Weapons';
-//todo: too many duplicate classes in this file!
-//todo: customize the chooseAction() to better fight the dragon
-//todo: update the `getASCIIStatus` function(s) to return X when dead and a unique character per class
-
-export class Warrior implements ICharacter {
- health: number = 5;
- position: number = 10;
- weapons: IWeapon[] = [];
- item?: IItem;
- feet = new Feet(this);
- classname(): CharacterClassName {
- return 'Warrior';
- }
- move(){
- this.feet.move();
- }
- constructor(public name: string, public key: number) {
+export class Warrior extends BaseCharacter {
+ constructor(name: string, key: number) {
+ super(name, key, 'Warrior', 'Greg');
equip(WarriorStartItem, this);
}
- chooseAction(): ICharacterActionDecision {
- return {
- attack: this.weapons[0]
- }
- }
- getASCIIStatus(): string {
- return "@";
- }
}
-export class Cleric implements ICharacter{
- health: number = 5;
- position: number = 10;
- weapons: IWeapon[] = [];
- item?: IItem;
- feet = new Feet(this);
- classname(): CharacterClassName {
- return 'Cleric';
- }
- move(){
- this.feet.move();
- }
- constructor(public name: string, public key: number) {
+export class Cleric extends BaseCharacter{
+ constructor(name: string, key: number) {
+ super(name, key, 'Cleric', 'Justin');
equip(ClericStartItem, this);
}
- chooseAction(): ICharacterActionDecision {
- return {
- attack: this.weapons[0]
- }
- }
- getASCIIStatus(): string {
- return "@";
- }
}
-export class Mage implements ICharacter {
- health: number = 5;
- position: number = 10;
- weapons: IWeapon[] = [];
- item?: IItem;
- feet = new Feet(this);
- classname(): CharacterClassName {
- return 'Mage';
- }
- constructor(public name: string, public key: number) {
+export class Mage extends BaseCharacter {
+ constructor(name: string, key: number) {
+ super(name, key, 'Mage', 'Nicho');
equip(MageStartItem, this);
}
- move(){
- this.feet.move();
- }
- chooseAction(): ICharacterActionDecision {
- return {
- attack: this.weapons[0]
- }
- }
- getASCIIStatus(): string {
- return "@";
- }
-}
-export class Thief implements ICharacter {
- health: number = 5;
- position: number = 10;
- weapons: IWeapon[] = [];
- item?: IItem;
- feet = new Feet(this);
- move(){
- this.feet.move();
- }
- classname(): CharacterClassName {
- return 'Thief';
- }
- constructor(public name: string, public key: number) {
- equip(ThiefStartItem, this);
- }
chooseAction(): ICharacterActionDecision {
- return {
- attack: this.weapons[0]
- }
- }
- getASCIIStatus(): string {
- return "@";
+ if (this.item && (this.item as IEnchantedItem).partyHealthBonus) // mage prioritizes healing
+ return { use: this.item };
+ return super.chooseAction();
}
}
-//todo: something about this class is code smell...
-export class Feet{
- constructor(private character: ICharacter){}
- move(){
- if (this.character.weapons.some(x => isMeleeWeapon(x))){
- this.character.position = Math.max(this.character.position - 5, 1);
- }
+export class Thief extends BaseCharacter {
+ constructor(name: string, key: number) {
+ super(name, key, 'Thief', 'Daniel');
+ equip(ThiefStartItem, this);
+ equip(ThiefAditionalStartItem, this);
}
-}
+}
\ No newline at end of file
diff --git a/src/workshop/Weapons.tsx b/src/workshop/Weapons.tsx
index 05e4f63..3e4b061 100644
--- a/src/workshop/Weapons.tsx
+++ b/src/workshop/Weapons.tsx
@@ -1,4 +1,4 @@
-import { IWeapon, IItem, IMeleeWeapon, IRangedWeapon, IConsumableItem, IEnchantedItem } from "../off-limits/IWeapons";
+import { IWeapon, IItem, IMeleeWeapon, IRangedWeapon, IConsumableItem, IEnchantedItem, isMeleeWeapon, isRangedWeapon } from "../off-limits/IWeapons";
// INTERFACE QUICK REFERENCE
// export interface IItem {
@@ -23,34 +23,94 @@ import { IWeapon, IItem, IMeleeWeapon, IRangedWeapon, IConsumableItem, IEnchante
// }
// WEAPON ARMORY
-// todo: add more and better weapons!
export class Club implements IMeleeWeapon {
name = 'Club';
damage = 1;
meleeRange = 1;
}
+export class Arrow implements IWeapon {
+ name = 'Arrow';
+ damage = 5;
+}
+
+export class Bow implements IRangedWeapon{
+ name: string = 'Bow';
+ damage: 0 = 0;
+ projectiles: Arrow[] = [];
+ constructor(startingArrows: number = 5) {
+ for (let i = 0; i < startingArrows; i++) {
+ this.projectiles.push(new Arrow());
+ }
+ }
+}
+
+export class Fireball implements IWeapon {
+ name = 'Fireball';
+ damage = 4;
+}
+export class FireStaff implements IRangedWeapon{
+ name: string = 'Fire Staff';
+ damage: 0 = 0;
+ projectiles: Fireball[] = [];
+ constructor(manaPoints: number) {
+ for (let i = 0; i < manaPoints; i++) {
+ this.projectiles.push(new Fireball());
+ }
+ }
+}
+
+export class Hammer implements IMeleeWeapon {
+ name = 'Hammer';
+ damage = 5;
+ meleeRange = 1;
+}
+export class Longsword implements IMeleeWeapon {
+ name = 'Longsword';
+ damage = 6;
+ meleeRange = 2;
+}
+export class Dagger implements IMeleeWeapon {
+ name = 'Dagger';
+ damage = 3;
+ meleeRange = 0;
+}
+
// ITEM VAULT
-// todo: add more and better items!
export class UselessAmulet implements IItem {
name = 'Useless Amulet';
}
-
+export class FireGloves implements IEnchantedItem {
+ name = 'Fire Gloves';
+ fireDamage = 1;
+}
+export class HealingWand implements IEnchantedItem {
+ name = 'Healing Wand';
+ fireDamage = 0;
+ partyHealthBonus = 1;
+}
+export class HealingPotion implements IConsumableItem {
+ name = 'Healing Potion';
+ healthBonus = 5;
+}
// ITEM ASSIGNMENTS
-// todo: assign starting items
-export const WarriorStartItem: IItem|undefined = new Club();
-export const ClericStartItem: IItem|undefined = new Club();
-export const MageStartItem: IItem|undefined = undefined;
-export const ThiefStartItem: IItem|undefined = new Club();
+export const WarriorStartItem: IItem|undefined = new Longsword();
+export const ClericStartItem: IItem|undefined = new Hammer();
+export const MageStartItem: IItem|undefined = new FireStaff(3);
+export const ThiefStartItem: IItem|undefined = new Bow(6);
+export const ThieAditionalfStartItem: IItem|undefined = new Dagger();
// TREASURE ASSIGNMENTS
-// todo: assign treasure from chests
export function GetItemsInTreasureChests(): IItem[]{
return [
- new UselessAmulet(), //this will be found by the warrior
- new UselessAmulet(), //this will be found by the cleric
- new UselessAmulet(), //this will be found by the mage
+ new HealingPotion(), //this will be found by the warrior
+ new FireGloves(), //this will be found by the cleric
+ new HealingWand(), //this will be found by the mage
new UselessAmulet(), //this will be found by the thief
];
}
+
+export function isWeaponFitForUse(weapon: IWeapon) {
+ return isMeleeWeapon(weapon) || (isRangedWeapon(weapon) && (weapon as IRangedWeapon).projectiles.length > 0);
+}
\ No newline at end of file