Modding and Adding Characters to Pressing Competition 2
From XGN Blog. Read it here
Introduction
Pressing Competition 2 or Typing Competition 2 is a Godot game which allows you to choose characters and fight against each other.
It's open-sourced under MIT here
You can find the showcase here
It's developed with Godot Mono 3.2.3
Steps
Preparation
What you need:
- Godot Mono 3.2.3 or later
- Source code from Github
In the tutorial, you want to get ZJS from Hell Hole Studios into the game.
0. Note
This tutorial is updated on 2021/1/30 and is aimed for version 1.5
This tutorial is updated again on 2021/3/6 to match 1.10
This tutorial is updated agin on 2021/3/13 to match 1.11
1. Import
Download source code and import it as Godot project. You are done!
Due to some Godot bugs, please ignore all errors.
2. Adding a Character Group
Create a new folder under res://script/characters/Groups
called HHS
and add a new script extending CharGroup
:
extends CharGroup
class_name HHSGroup #Group Class Name
func _init().("Hell Hole Studios",[
ZJS.new() # We will define ZJS later
]):
pass
Provide two parameters to super._init()
:
name
:String, the name of the groupchars
:Array, theCharacterBase
s in this group
Register your group at res://script/characters/AvailableCharacters.gd
:
extends Node
# Contains all available characters in groups
var chars=[
MainGroup.new(),
EoSDGroup.new(),
PCBGroup.new(),
INGroup.new(),
PoFVGroup.new(),
HHSGroup.new() #Add here
]
3. Adding a character
Create file res://script/characters/Groups/HHS/ZJS.gd
extending CharacterBase
:
Let's say ZJS's skill is to deal 1000 damage to himself
extends CharacterBase
class_name ZJS
# Only after 1.10
func onSkillA(me,sub,enemy,enemySub,scene,isSub):
scene.emit_signal("damage_given",me,1000)
# Before 1.10:
# func onSkill(me,enemy,scene):
# scene.emit_signal("damage_given",me,1000)
func _init():
self.maxhp=450
self.atk=1
self.name="ZJS"
self.skillCost=500
self.image="res://pic/ZJS.png"
self.heal=1
self.version=1 # Only after 1.10
self.desc="""
Skill: Crazy Dance
Dance crazily and hurt yourself by 1000 HP
"""
3.1 The init function
Define the following properties here:
maxhp
:Int(1-1000)atk
:Int(1-5)name
:StringskillCost
:Int(1-2000). Don't set it to 0 unless it's a passive skill.image
:String Image pathheal
:Int(1-5)desc
:String DescriptionskillType
:Int(0-1). Optional. Set this to 1 to set the skill to be a passive type.version
:Int(0-1). Optional. Set this to 1 for a more advanced skill overwrite. 1 is recommended for >=1.10 usersnosub
:Bool Optional. Setting this to true will make the character unavailable for minions
3.2 The onSkillA function
onSkillA
function comes with 6 parameters. It's called when player uses a skill. It's also called every frame if it's a passive skill. This method won't be called unless version
is set to 1.
me
: Character the skill user side's mastersub
: Character the skill user side's minionenemy
: Character the skill enemy side's masterenemySub
: Character the skill enemy side's minionscene
: GameSubview current sceneisSub
: Bool whether the skill was called by a minion
3.3 The onSkill function
onSkill
is an easier version of onSkillA
. This method won't be called unless version
is set to 0(default).
me
:Character the skill userenemy
:Character skill user's enemyscene
:GameSubview current GameSubview scene
3.4 Character Properties
Let's take a look at what's in the class Character
so you know what you can do:
# Character.gd
extends Object
class_name Character
var hp: int # Current HP.
var base: CharacterBase # It's character base. READ-ONLY
var skillGauge: int # Current Skill Gauge
var status: Array # Current Buffs
var maxhp: int # Current Max HP
var skillCost: int # Current Skill Cost
var atk: int # Current Attack
var heal: int # Current Heal
var role: int #Role: 0 = player 1, 1 = player 2. MODIFY IT MAY CAUSE UNEXPECTED BEHAVIOUR
var vars: Dictionary = {} #Custom Variables you can store
var ai: int #AI Level 0=player control
CharacterBase uses Singleton design principle so:
base
is read-only. Writing it will cause the next round to be corrupted. Write to corresponding variables instead
Do not use self
or internal variables in onSkill. Writing it will cause the next round to be corrupted. Write to me
or me.vars
instead
Use scene.emit_signal("damage_given",...,...)
to deal damage or the HP will not be displayed correctly
Some common scenario
- Increase attack by 1:
me.atk+=1
- Double skill cost:
enemy.skillCost*=2
- Adding Status:
enemy.status.append(GDBuff.new("test",600,1,2))
- Deal 60 damage:
scene.emit_signal("damage_given",enemy,60)
- Heal 60 HP:
scene.emit_signal("damage_given",me,-60)
- Writing to user variable:
me.vars["123"]+=1
- Writing to scene objects: Lorelei:6
- Initalizing a variable fast: Koishi:6
- Checking minion: Satono:6
- Calling others' skill: Rin:8
3.5 Key controls(>=1.5)
- Use
Character.checkAction("attack/defense/skill/gauge")
to check if a character is attacking/healing/gauge-filling/using skill at the same frame this is called. Character.key
holds the bitmask for the actions at this frame- Use
Character.checkA1("attack/defense/skill/gauge")
to check if a key is just pressed - Use
Character.checkA2("attack/defense/skill/gauge")
to check if a key is pressed/hold - Use
Character.checkB1("attack/defense/skill/gauge",default)
to check if a key is just pressed or return default if it's AI - Use
Character.checkB2("attack/defense/skill/gauge",default)
to check if a key is pressed/hold or return default if it's AI
3.6 Adding pictures
The last step is to put your picture in the path you set to. Make sure it's facing right. It's recommended to put it under res://pic
4. Status
Use status to create lengthy events!
4.1 Built-in Status
System comes with built-in status:
GDBuff.new(name,duration,damage,interval)
will inflict a status calledname
and isduration
frames long. It will causedamage
damage everyinterval
frames-
GSBuff.new(name,duration,damage,interval)
will inflict a status calledname
and isduration
frames long. It will losedamage
skill gauge everyinterval
frames -
GDRBuff.new(name,duration,da,db,interval)
will inflict a status calledname
and isduration
frames long. It will causeda
todb
damage randomly everyinterval
frames
4.2 Custom Status
Custom Status should extends Status
:
extends Status
class_name TimestopBuff #Class name
func onCast(me, scene): #Required. Called every frame. me is a Character and scene is a GameSubview
me.skillGauge=0
func _init():
self.name="Timestop" #Required
self.time=300 #Required. In frames
This is another example:
extends Status
class_name YuyukoBuff
func onCast(me, scene):
if me.hp<=me.maxhp*0.2:
scene.emit_signal("damage_given",me,-me.maxhp)
self.time=0
func _init():
self.name="Barrier"
self.time=600
Note that using self and internal variables are okay:
extends Status
class_name GDBuff
var val=0
var interval=0
var damage=0
func onCast(me, scene):
val+=1
if val%interval==0:
scene.emit_signal("damage_given",me,damage)
func _init(name,time,damage,interval):
self.name=name
self.time=time
self.interval=interval
self.damage=damage
5. Global & Config
scene.Global
contains the Global variables, use them if you need:
# Global.gd
extends Node
var p1: Character=Character.new(Reimu.new()) #Player 1
var p2: Character=Character.new(Reimu.new()) #Minion 1
var s1: Character=Character.new(Reimu.new()) #Player 2
var s2: Character=Character.new(Reimu.new()) #Minion 2
var incMana = 2 #gauge increase speed (per frame)
var decMana = 1 #gauge decrease speed (per frame)
var death = 0 #Sudden Death damage
# Config Element
var phaseLength = 60 # Phase Length in seconds
var musicVol = 0 # Music Volume in dB. Read https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html#decibel-scale
var HPMul = 1 # HP Multiplier.
6. Minions (>=1.10)
Minions are a new concept and game mechanics in 1.10. The minion is stored in Global.s1
and Global.s2
as a character. It's basically a character with their HP,heal,atk unused, so don't deal damage or cast status on them. You can set nosub
to true to prevent a character being selected as minion.
The minion gauge is stored in minion's skillGauge
variable and the threshold is in skillCost
.
When master attacks, gauge will increase by 10. When master gets hurt, gauge will increase by damage
.
7. Refer to source code for 100+ examples!
8. Build and release!
Appendix
You should release your own version of PC2 with credits and link to the original version. You can also have your characters included in the main version with a pull request.
So, with that being said, enjoy modding and creating!
版权声明:
作者:XGN
链接:https://blog.hellholestudios.top/archives/517
来源:Hell Hole Studios Blog
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论