Game Character Development
Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 64 of 65.
Challenge
MediumBuild the characters of a role-playing game: an abstract Character, three classes that extend it, and abilities that characters learn.
Character in character.gd is an @abstract class:
- Properties:
character_name(from the constructor),level1,max_healthandhealth100.0,max_manaandmana50,strength,intelligenceandagility10,defense5, andabilities, anArray[Ability]. Health values arefloat, the other statsint. - A signal
defeated(character_name: String). - Abstract methods
class_title() -> Stringandattack_power() -> float. level_up() -> String:+1level,+10maximum health,+5maximum mana,+1strength, intelligence, agility and defense; health and mana are refilled to their maximum. ReturnsNAME reached level N!.attack(target): printsNAME attacks TARGET, then returnstarget.take_damage(attack_power()).take_damage(amount) -> float: the damage isamount - defense * 0.5, but at least1.0; health goes down by it and never below0.0. It printsNAME takes D damage (HP H)with one decimal each, emitsdefeatedwhen this hit took the health to0, and returns the damage.is_alive(): health above0.learn_ability(ability)appends toabilities.use_ability(index, target) -> float: for an index outsideabilities, printsNAME has no ability INDEXand returns0.0; otherwise returns what the ability'suse()returns._to_string():NAME the TITLE (level L): HP H/MAX, mana M/MAX, STR S, INT I, AGI A, DEF D, health with one decimal.
Each subclass calls the parent constructor, then sets its own starting stats (the others keep the base values), returns its class name from class_title() and overrides level_up() with super():
Warrior: health150.0, mana30, strength15, intelligence5, defense8; attack powerstrength * 1.2; on level up also+2strength and+10maximum health (health refilled).Mage: health80.0, mana150, strength5, intelligence20, defense3; attack powerintelligence * 0.4; after a normal attack it gets5mana back (never above the maximum); on level up also+2intelligence and+15maximum mana (mana refilled).Rogue: health100.0, mana60, strength12, intelligence8, agility18, defense4; attack powerstrength * 0.5 + agility * 0.7; on level up also+2agility.
Ability in ability.gd: the constructor takes ability_name, mana_cost (int) and damage (float). use(user, target) -> float prints USER does not have enough mana for ABILITY and returns 0.0 when the user's mana is below the cost. Otherwise it takes the mana, prints USER casts ABILITY on TARGET and returns target.take_damage(damage + user.intelligence * 0.5).
The supplied code connects to every character's defeated signal and prints NAME is defeated!. For this input:
hero warrior|Aragorn
hero rogue|Orc
attack Aragorn|Orc
attack Orc|Aragorn
level Aragorn
status Aragornthe output is:
Aragorn attacks Orc
Orc takes 16.0 damage (HP 84.0)
Orc attacks Aragorn
Aragorn takes 14.6 damage (HP 135.4)
Aragorn reached level 2!
Aragorn the Warrior (level 2): HP 170.0/170.0, mana 35/35, STR 18, INT 6, AGI 11, DEF 9Your code goes in character.gd, warrior.gd, mage.gd, rogue.gd and ability.gd. main.gd holds the supplied input/output code and cannot be edited.
Try it yourself
extends Node
func _ready():
# Supplied input/output code: keep it as it is
var heroes := {}
var line := OS.read_string_from_stdin()
while line != "":
var parts := line.split(" ", true, 1)
var f: PackedStringArray = parts[1].split("|") if parts.size() > 1 else PackedStringArray()
match parts[0]:
"hero":
var hero: Character
match f[0]:
"warrior":
hero = Warrior.new(f[1])
"mage":
hero = Mage.new(f[1])
"rogue":
hero = Rogue.new(f[1])
hero.defeated.connect(func(who): print(who, " is defeated!"))
heroes[f[1]] = hero
"learn":
heroes[f[0]].learn_ability(Ability.new(f[1], int(f[2]), float(f[3])))
"attack":
heroes[f[0]].attack(heroes[f[1]])
"cast":
heroes[f[0]].use_ability(int(f[1]), heroes[f[2]])
"level":
print(heroes[f[0]].level_up())
"status":
print(heroes[f[0]])
line = OS.read_string_from_stdin()
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self KeywordMethodsPropertiesConstructor (_init)Recap - Simple Calculator4Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator2Class Properties
Instance vs Static VariablesStatic FunctionsProperty SettersProperty GettersPrivate by ConventionRecap - Bank Account Manager5Encapsulation
Public and Private MembersValidation with SettersInformation HidingRead-Only PropertiesRecap - Student Records System8Advanced OOP Concepts
Composition vs InheritanceInner ClassesNodes as ComponentsRefCounted vs NodeEnums in Classes11Project: Library Management
Project OverviewBook and User Classes3Inheritance
Basic InheritanceThe super KeywordMethod OverridingInheritance ChainsType Checks with isRecap - Employee Hierarchy6Special Methods
Virtual Methods IntroductionThe _to_string MethodCustom IteratorsRecap - Custom List9Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy PatternPractice on your own: Online GDScript compiler