replace with multiplayer-coop files
This commit is contained in:
656
src/scripts/inspiration_scripts/character_stats.gd
Normal file
656
src/scripts/inspiration_scripts/character_stats.gd
Normal file
@@ -0,0 +1,656 @@
|
||||
class_name CharacterStats
|
||||
extends Resource
|
||||
|
||||
signal health_changed(new_health: float, max_health: float)
|
||||
signal mana_changed(new_mana: float, max_mana: float)
|
||||
signal level_changed(new_level: int)
|
||||
signal xp_changed(new_xp: float, xp_to_next: float)
|
||||
signal no_health
|
||||
|
||||
signal character_changed(char: CharacterStats)
|
||||
|
||||
signal signal_drop_item(item: Item)
|
||||
|
||||
var character_type: String = "enemy"
|
||||
@export var level: int = 1
|
||||
@export var character_name: String = ""
|
||||
@export var xp: float = 0
|
||||
@export var hp: float = 30.0
|
||||
@export var mp: float = 20.0
|
||||
|
||||
# default skin is human1
|
||||
var skin:String = "res://assets/gfx/Puny-Characters/Layer 0 - Skins/Human1.png"
|
||||
# default no values for these:
|
||||
var facial_hair:String = ""
|
||||
var facial_hair_color:Color = Color.WHITE
|
||||
var hairstyle:String = ""
|
||||
var hair_color:Color = Color.WHITE
|
||||
var eyes:String = ""
|
||||
var eye_lashes:String = ""
|
||||
var add_on:String = ""
|
||||
|
||||
var bonusmaxhp: float = 0.0
|
||||
var bonusmaxmp: float = 0.0
|
||||
|
||||
@export var coin: int = 0
|
||||
@export var is_invulnerable: bool = false
|
||||
var kills: int = 0
|
||||
var deaths: int = 0
|
||||
|
||||
#calculated values
|
||||
var stats:Array = [
|
||||
5,
|
||||
4,
|
||||
5,
|
||||
3,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
10
|
||||
]
|
||||
|
||||
var inventory: Array = []
|
||||
|
||||
# mainhand, offhand, headgear, body, feet, accessory (6 total)
|
||||
var equipment:Dictionary = {
|
||||
"mainhand": null,
|
||||
"offhand": null,
|
||||
"headgear": null,
|
||||
"armour": null,
|
||||
"boots": null,
|
||||
"accessory": null
|
||||
}
|
||||
|
||||
@export var baseStats = {
|
||||
"str": 10,
|
||||
"dex": 10,
|
||||
"int": 10,
|
||||
"end": 10,
|
||||
"wis": 10,
|
||||
"cha": 10,
|
||||
"lck": 10
|
||||
}
|
||||
|
||||
@export var def: int = 0
|
||||
|
||||
@export var resistances = {
|
||||
"fire": 0,
|
||||
"cold": 0,
|
||||
"lightning": 0,
|
||||
"poison": 0,
|
||||
"magic": 0,
|
||||
"holy": 0,
|
||||
"dark": 0
|
||||
}
|
||||
|
||||
func getCalculatedStats():
|
||||
var _res = {
|
||||
"str": self.str,
|
||||
"dex": self.dex,
|
||||
"int": self.int,
|
||||
"end": self.end,
|
||||
"wis": self.wis,
|
||||
"cha": self.cha,
|
||||
"lck": self.lck,
|
||||
"damage": self.damage,
|
||||
"defense": self.defense
|
||||
}
|
||||
if equipment["mainhand"] != null:
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
func get_pass(iStr:String):
|
||||
var cnt = 0
|
||||
if equipment["mainhand"] != null:
|
||||
for key in equipment["mainhand"].modifiers.keys():
|
||||
if key == iStr:
|
||||
cnt += equipment["mainhand"].modifiers[key]
|
||||
pass
|
||||
pass
|
||||
if equipment["offhand"] != null:
|
||||
for key in equipment["offhand"].modifiers.keys():
|
||||
if key == iStr:
|
||||
cnt += equipment["offhand"].modifiers[key]
|
||||
pass
|
||||
pass
|
||||
if equipment["headgear"] != null:
|
||||
for key in equipment["headgear"].modifiers.keys():
|
||||
if key == iStr:
|
||||
cnt += equipment["headgear"].modifiers[key]
|
||||
pass
|
||||
pass
|
||||
if equipment["armour"] != null:
|
||||
for key in equipment["armour"].modifiers.keys():
|
||||
if key == iStr:
|
||||
cnt += equipment["armour"].modifiers[key]
|
||||
pass
|
||||
pass
|
||||
if equipment["boots"] != null:
|
||||
for key in equipment["boots"].modifiers.keys():
|
||||
if key == iStr:
|
||||
cnt += equipment["boots"].modifiers[key]
|
||||
pass
|
||||
pass
|
||||
if equipment["accessory"] != null:
|
||||
for key in equipment["accessory"].modifiers.keys():
|
||||
if key == iStr:
|
||||
cnt += equipment["accessory"].modifiers[key]
|
||||
pass
|
||||
pass
|
||||
return cnt
|
||||
|
||||
# Derived stats as properties
|
||||
var maxhp: float:
|
||||
get:
|
||||
return (baseStats.end + get_pass("end")) * 3.0 + bonusmaxhp + get_pass("maxhp")
|
||||
|
||||
var maxmp: float:
|
||||
get:
|
||||
return (baseStats.wis + get_pass("wis")) * 2.0 + bonusmaxmp + get_pass("maxmp")
|
||||
|
||||
var damage: float:
|
||||
get:
|
||||
return (baseStats.str + get_pass("str")) * 0.2 + get_pass("dmg")
|
||||
|
||||
var defense: float:
|
||||
get:
|
||||
return ((baseStats.end + get_pass("end")) * 0.3) + get_pass("def")
|
||||
|
||||
var spell_amp: float:
|
||||
get:
|
||||
return (baseStats.int + get_pass("int")) * 0.5
|
||||
|
||||
var move_speed: float:
|
||||
get:
|
||||
return 2.0 + ((baseStats.dex + get_pass("dex")) * 0.01)
|
||||
|
||||
var attack_speed: float:
|
||||
get:
|
||||
return 1.0 + ((baseStats.dex + get_pass("dex")) * 0.04)
|
||||
|
||||
var sight: float:
|
||||
get:
|
||||
return 5.0 + ((baseStats.int + get_pass("int")) * 0.05)
|
||||
|
||||
var crit_chance: float:
|
||||
get:
|
||||
return (baseStats.lck + get_pass("lck")) * 1.2
|
||||
|
||||
var xp_to_next_level: float:
|
||||
get:
|
||||
return pow(level * 10, 1.5)
|
||||
|
||||
func _init() -> void:
|
||||
hp = maxhp
|
||||
mp = maxmp
|
||||
|
||||
func add_xp(amount: float) -> void:
|
||||
xp += amount
|
||||
xp_changed.emit(xp, xp_to_next_level)
|
||||
|
||||
while xp >= xp_to_next_level:
|
||||
level_up()
|
||||
|
||||
# instead of automatically update all stats, maybe let the player choose which stats to level up.
|
||||
func level_up() -> void:
|
||||
level += 1
|
||||
xp -= xp_to_next_level
|
||||
|
||||
# Increase stats
|
||||
baseStats.str += 1
|
||||
baseStats.dex += 1
|
||||
baseStats.int += 1
|
||||
baseStats.end += 1
|
||||
baseStats.wis += 1
|
||||
baseStats.cha += 1
|
||||
baseStats.lck += 1
|
||||
|
||||
# Restore health and mana on level up
|
||||
hp = maxhp
|
||||
mp = maxmp
|
||||
|
||||
level_changed.emit(level)
|
||||
health_changed.emit(hp, maxhp)
|
||||
mana_changed.emit(mp, maxmp)
|
||||
|
||||
func modify_health(amount: float) -> void:
|
||||
hp = clamp(hp + amount, 0, maxhp)
|
||||
health_changed.emit(hp, maxhp)
|
||||
character_changed.emit(self)
|
||||
|
||||
func modify_mana(amount: float) -> void:
|
||||
mp = clamp(mp + amount, 0, maxmp)
|
||||
mana_changed.emit(mp, maxmp)
|
||||
character_changed.emit(self)
|
||||
|
||||
func calculate_damage(base_damage: float, is_magical: bool = false) -> float:
|
||||
if is_magical:
|
||||
return base_damage * (1 - (resistances.magic / 100.0))
|
||||
return base_damage * (1 - (defense / 100.0))
|
||||
|
||||
func take_damage(amount: float, is_magical: bool = false) -> float:
|
||||
var actual_damage = calculate_damage(amount, is_magical)
|
||||
modify_health(-actual_damage)
|
||||
if hp <= 0:
|
||||
no_health.emit() # Emit when health reaches 0
|
||||
character_changed.emit(self)
|
||||
return actual_damage
|
||||
|
||||
func heal(amount: float) -> void:
|
||||
modify_health(amount)
|
||||
|
||||
func use_mana(amount: float) -> bool:
|
||||
if mp >= amount:
|
||||
modify_mana(-amount)
|
||||
return true
|
||||
return false
|
||||
|
||||
func restore_mana(amount: float) -> void:
|
||||
modify_mana(amount)
|
||||
|
||||
func saveInventory() -> Array:
|
||||
var inventorySave = []
|
||||
for it:Item in inventory:
|
||||
inventorySave.push_back(it.save())
|
||||
return inventorySave
|
||||
|
||||
func saveEquipment() -> Dictionary:
|
||||
var eqSave = {
|
||||
"mainhand": equipment["mainhand"].save() if equipment["mainhand"] != null else null,
|
||||
"offhand": equipment["offhand"].save() if equipment["offhand"] != null else null,
|
||||
"headgear": equipment["headgear"].save() if equipment["headgear"] != null else null,
|
||||
"armour": equipment["armour"].save() if equipment["armour"] != null else null,
|
||||
"boots": equipment["boots"].save() if equipment["boots"] != null else null,
|
||||
"accessory": equipment["accessory"].save() if equipment["accessory"] != null else null
|
||||
}
|
||||
return eqSave
|
||||
|
||||
func loadInventory(iArr: Array):
|
||||
inventory.clear() # remove previous content
|
||||
for iDic in iArr:
|
||||
if iDic != null:
|
||||
inventory.push_back( Item.new(iDic) )
|
||||
pass
|
||||
|
||||
func loadEquipment(iDic: Dictionary):
|
||||
equipment["mainhand"] = Item.new(iDic.get("mainhand")) if iDic.has("mainhand") and iDic.get("mainhand") != null else null
|
||||
equipment["offhand"] = Item.new(iDic.get("offhand")) if iDic.has("offhand") and iDic.get("offhand") != null else null
|
||||
equipment["headgear"] = Item.new(iDic.get("headgear")) if iDic.has("headgear") and iDic.get("headgear") != null else null
|
||||
equipment["armour"] = Item.new(iDic.get("armour")) if iDic.has("armour") and iDic.get("armour") != null else null
|
||||
equipment["boots"] = Item.new(iDic.get("boots")) if iDic.has("boots") and iDic.get("boots") != null else null
|
||||
equipment["accessory"] = Item.new(iDic.get("accessory")) if iDic.has("accessory") and iDic.get("accessory") != null else null
|
||||
pass
|
||||
|
||||
func save() -> Dictionary:
|
||||
var json = {
|
||||
"level": level,
|
||||
"character_type": character_type,
|
||||
"character_name": character_name,
|
||||
|
||||
"baseStats": baseStats,
|
||||
|
||||
"hp": hp,
|
||||
"mp": mp,
|
||||
"bonusmaxhp": bonusmaxhp,
|
||||
"bonusmaxmp": bonusmaxmp,
|
||||
|
||||
"exp": xp,
|
||||
"coin": coin,
|
||||
|
||||
"kills": kills,
|
||||
"deaths": deaths,
|
||||
|
||||
"skin": skin,
|
||||
"facial_hair": facial_hair,
|
||||
"hairstyle": hairstyle,
|
||||
"eyes": eyes,
|
||||
"eye_lashes": eye_lashes,
|
||||
"add_on": add_on,
|
||||
|
||||
"facial_hair_color": facial_hair_color.to_html(true),
|
||||
"hair_color": hair_color.to_html(true),
|
||||
|
||||
"inventory": saveInventory(),
|
||||
"equipment": saveEquipment()
|
||||
}
|
||||
|
||||
return json
|
||||
|
||||
func load(iDic: Dictionary) -> void:
|
||||
if iDic.has("level"):
|
||||
level = iDic.get("level")
|
||||
if iDic.has("character_type"):
|
||||
character_type = iDic.get("character_type")
|
||||
if iDic.has("character_name"):
|
||||
character_name = iDic.get("character_name")
|
||||
if iDic.has("hp"):
|
||||
hp = iDic.get("hp")
|
||||
if iDic.has("mp"):
|
||||
mp = iDic.get("mp")
|
||||
if iDic.has("bonusmaxhp"):
|
||||
bonusmaxhp = iDic.get("bonusmaxhp")
|
||||
if iDic.has("bonusmaxmp"):
|
||||
bonusmaxmp = iDic.get("bonusmaxmp")
|
||||
if iDic.has("baseStats"):
|
||||
baseStats = iDic.get("baseStats")
|
||||
if iDic.has("coin"):
|
||||
coin = iDic.get("coin")
|
||||
if iDic.has("exp"):
|
||||
xp = iDic.get("exp")
|
||||
if iDic.has("kills"):
|
||||
kills = iDic.get("kills")
|
||||
if iDic.has("deaths"):
|
||||
deaths = iDic.get("deaths")
|
||||
|
||||
inventory.clear()
|
||||
if iDic.has("inventory"):
|
||||
loadInventory(iDic.get("inventory"))
|
||||
|
||||
# reset equipment
|
||||
equipment = {
|
||||
"mainhand": null,
|
||||
"offhand": null,
|
||||
"headgear": null,
|
||||
"armour": null,
|
||||
"boots": null,
|
||||
"accessory": null
|
||||
}
|
||||
if iDic.has("equipment"):
|
||||
loadEquipment(iDic.get("equipment"))
|
||||
if iDic.has("skin"):
|
||||
skin = iDic.get("skin")
|
||||
if iDic.has("facial_hair"):
|
||||
facial_hair = iDic.get("facial_hair")
|
||||
if iDic.has("hairstyle"):
|
||||
hairstyle = iDic.get("hairstyle")
|
||||
if iDic.has("eyes"):
|
||||
eyes = iDic.get("eyes")
|
||||
if iDic.has("eye_lashes"):
|
||||
eye_lashes = iDic.get("eye_lashes")
|
||||
if iDic.has("add_on"):
|
||||
add_on = iDic.get("add_on")
|
||||
if iDic.has("facial_hair_color"):
|
||||
facial_hair_color = Color(iDic.get("facial_hair_color"))
|
||||
if iDic.has("hair_color"):
|
||||
hair_color = Color(iDic.get("hair_color"))
|
||||
|
||||
pass
|
||||
'
|
||||
func calculateStats():
|
||||
for i in stats.size():
|
||||
stats[i] = baseStats[i]
|
||||
pass
|
||||
stats[PlayerData.STAT.STAT_ATK] = 1 # defaults to 1
|
||||
|
||||
# add equipment modifiers:
|
||||
for eq in equipped:
|
||||
if eq >= 0:
|
||||
for modifier in equipment[eq]["equipment"].modifiers:
|
||||
stats[modifier["stat"] as PlayerData.STAT] += modifier["change"]
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
pass'
|
||||
|
||||
func add_coin(iAmount:int):
|
||||
coin += iAmount
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func drop_item(iItem:Item):
|
||||
var index = 0
|
||||
for item in inventory:
|
||||
if item == iItem:
|
||||
break
|
||||
index+=1
|
||||
inventory.remove_at(index)
|
||||
emit_signal("signal_drop_item", iItem)
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func drop_equipment(iItem:Item):
|
||||
unequip_item(iItem, false)
|
||||
# directly remove the item from the inventory
|
||||
drop_item(iItem)
|
||||
pass
|
||||
|
||||
func add_item(iItem:Item):
|
||||
self.inventory.push_back(iItem)
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func unequip_item(iItem:Item, updateChar:bool = true):
|
||||
if iItem.equipment_type == Item.EquipmentType.NONE:
|
||||
return
|
||||
self.inventory.push_back(iItem)
|
||||
match iItem.equipment_type:
|
||||
Item.EquipmentType.MAINHAND:
|
||||
equipment["mainhand"] = null
|
||||
pass
|
||||
Item.EquipmentType.OFFHAND:
|
||||
equipment["offhand"] = null
|
||||
pass
|
||||
Item.EquipmentType.HEADGEAR:
|
||||
equipment["headgear"] = null
|
||||
pass
|
||||
Item.EquipmentType.ARMOUR:
|
||||
equipment["armour"] = null
|
||||
pass
|
||||
Item.EquipmentType.BOOTS:
|
||||
equipment["boots"] = null
|
||||
pass
|
||||
Item.EquipmentType.ACCESSORY:
|
||||
equipment["accessory"] = null
|
||||
pass
|
||||
pass
|
||||
if updateChar:
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func forceUpdate():
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func equip_item(iItem:Item):
|
||||
if iItem.equipment_type == Item.EquipmentType.NONE:
|
||||
return
|
||||
match iItem.equipment_type:
|
||||
Item.EquipmentType.MAINHAND:
|
||||
if equipment["mainhand"] != null:
|
||||
self.inventory.push_back(equipment["mainhand"])
|
||||
# If we equip different weapon than bow and we have ammunition in offhand, remove, the offhand.
|
||||
# If we equip two handed weapon, remove offhand...
|
||||
#if equipment["offhand"] != null:
|
||||
#(equipment["offhand"] as Item).equipment_type == Item.WeaponType.AMMUNITION
|
||||
#if iItem.WeaponType.BOW
|
||||
|
||||
equipment["mainhand"] = iItem
|
||||
pass
|
||||
pass
|
||||
Item.EquipmentType.OFFHAND:
|
||||
if equipment["offhand"] != null:
|
||||
self.inventory.push_back(equipment["offhand"])
|
||||
equipment["offhand"] = iItem
|
||||
pass
|
||||
pass
|
||||
Item.EquipmentType.HEADGEAR:
|
||||
if equipment["headgear"] != null:
|
||||
self.inventory.push_back(equipment["headgear"])
|
||||
equipment["headgear"] = iItem
|
||||
pass
|
||||
pass
|
||||
|
||||
Item.EquipmentType.ARMOUR:
|
||||
if equipment["armour"] != null:
|
||||
self.inventory.push_back(equipment["armour"])
|
||||
equipment["armour"] = iItem
|
||||
pass
|
||||
pass
|
||||
Item.EquipmentType.BOOTS:
|
||||
if equipment["boots"] != null:
|
||||
self.inventory.push_back(equipment["boots"])
|
||||
equipment["boots"] = iItem
|
||||
pass
|
||||
pass
|
||||
Item.EquipmentType.ACCESSORY:
|
||||
if equipment["accessory"] != null:
|
||||
self.inventory.push_back(equipment["accessory"])
|
||||
equipment["accessory"] = iItem
|
||||
pass
|
||||
pass
|
||||
self.inventory.remove_at(self.inventory.find(iItem))
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func setSkin(iValue:int):
|
||||
if iValue < 0 or iValue > 6:
|
||||
return
|
||||
skin = "res://assets/gfx/Puny-Characters/Layer 0 - Skins/Human" + str(iValue+1) + ".png"
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func setFacialHair(iType:int):
|
||||
if iType < 0 or iType > 3:
|
||||
return
|
||||
|
||||
match iType:
|
||||
0:
|
||||
facial_hair = ""
|
||||
emit_signal("character_changed", self)
|
||||
return
|
||||
1:
|
||||
facial_hair = "res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Facial Hairstyles/Beardstyle1"
|
||||
pass
|
||||
2:
|
||||
facial_hair = "res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Facial Hairstyles/Beardstyle2"
|
||||
pass
|
||||
3:
|
||||
facial_hair = "res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Facial Hairstyles/Mustache1"
|
||||
pass
|
||||
facial_hair += "White.png"
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
|
||||
func setHair(iType:int):
|
||||
if iType < 0 or iType > 12:
|
||||
return
|
||||
if iType == 0:
|
||||
hairstyle = ""
|
||||
emit_signal("character_changed", self)
|
||||
return
|
||||
hairstyle = "res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Hairstyles/FHairstyle" + str(iType)
|
||||
if iType >= 5: # male hairstyles
|
||||
hairstyle = "res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Hairstyles/MHairstyle" + str(iType-4)
|
||||
hairstyle += "White.png"
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func setEyeLashes(iEyeLashes: int):
|
||||
if iEyeLashes < 0 or iEyeLashes > 8:
|
||||
return
|
||||
if iEyeLashes == 0:
|
||||
eye_lashes = ""
|
||||
else:
|
||||
match iEyeLashes:
|
||||
1:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/FEyelash1.png"
|
||||
pass
|
||||
2:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/FEyelash2.png"
|
||||
pass
|
||||
3:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/FEyelash3.png"
|
||||
pass
|
||||
4:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/MEyelash1.png"
|
||||
pass
|
||||
5:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/MEyelash2.png"
|
||||
pass
|
||||
6:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/NEyelash1.png"
|
||||
pass
|
||||
7:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/NEyelash2.png"
|
||||
pass
|
||||
8:
|
||||
eye_lashes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/NEyelash3.png"
|
||||
pass
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func setEyes(iEyes: int):
|
||||
if iEyes < 0 or iEyes > 14:
|
||||
return
|
||||
if iEyes == 0:
|
||||
eyes = ""
|
||||
else:
|
||||
match iEyes:
|
||||
1:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorBlack.png"
|
||||
pass
|
||||
2:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorBlue.png"
|
||||
pass
|
||||
3:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorCyan.png"
|
||||
pass
|
||||
4:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorDarkBlue.png"
|
||||
pass
|
||||
5:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorDarkCyan.png"
|
||||
pass
|
||||
6:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorDarkLime.png"
|
||||
pass
|
||||
7:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorDarkRed.png"
|
||||
pass
|
||||
8:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorFullBlack.png"
|
||||
pass
|
||||
9:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorFullWhite.png"
|
||||
pass
|
||||
10:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorGray.png"
|
||||
pass
|
||||
11:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorLightLime.png"
|
||||
pass
|
||||
12:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorOrange.png"
|
||||
pass
|
||||
13:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorRed.png"
|
||||
pass
|
||||
14:
|
||||
eyes = "res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorYellow.png"
|
||||
pass
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func setEars(iEars: int):
|
||||
if iEars < 0 or iEars > 7:
|
||||
return
|
||||
if iEars == 0:
|
||||
add_on = ""
|
||||
else:
|
||||
add_on = "res://assets/gfx/Puny-Characters/Layer 7 - Add-ons/Elf Add-ons/ElfEars" + str(iEars) + ".png"
|
||||
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
|
||||
func setFacialHairColor(iColor: Color):
|
||||
facial_hair_color = iColor
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
func setHairColor(iColor:Color):
|
||||
hair_color = iColor
|
||||
emit_signal("character_changed", self)
|
||||
pass
|
||||
1
src/scripts/inspiration_scripts/character_stats.gd.uid
Normal file
1
src/scripts/inspiration_scripts/character_stats.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dvcubtup4odug
|
||||
161
src/scripts/inspiration_scripts/coin.gd
Normal file
161
src/scripts/inspiration_scripts/coin.gd
Normal file
@@ -0,0 +1,161 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
var friction = 8.0 # Lower values make the slowdown more gradual
|
||||
var is_collected = false
|
||||
var bounceTimer = 0.0
|
||||
var timeBeforePickup = 0.8
|
||||
@onready var damage_number_scene = preload("res://assets/scripts/components/damage_number.tscn")
|
||||
|
||||
# Z-axis variables
|
||||
var positionZ = 4.0 # Start slightly in the air
|
||||
var velocityZ = 10.0 # Initial upward velocity
|
||||
var accelerationZ = -300.0 # Gravity
|
||||
var bounceRestitution = 0.6 # How much bounce energy is retained (0-1)
|
||||
var minBounceVelocity = 40.0 # Minimum velocity needed to bounce
|
||||
|
||||
var bodyToPickUp:Node2D = null
|
||||
|
||||
var sync_position := Vector2()
|
||||
var sync_velocity := Vector2()
|
||||
var sync_positionZ: float = 4.0
|
||||
var sync_velocityZ: float = 10.0
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("coins")
|
||||
$Area2DCollision.set_deferred("monitoring", false)
|
||||
update_sprite_scale()
|
||||
|
||||
# Start animation
|
||||
$AnimationPlayer.play("idle")
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if bodyToPickUp != null and !is_collected and timeBeforePickup == 0 and positionZ < 5:
|
||||
visible = false
|
||||
$SfxCoinCollect.play()
|
||||
is_collected = true
|
||||
$Area2DCollision.set_deferred("monitoring", false)
|
||||
|
||||
var damage_number = damage_number_scene.instantiate() as Label
|
||||
if damage_number:
|
||||
get_tree().current_scene.add_child(damage_number)
|
||||
damage_number.global_position = global_position + Vector2(0, -16)
|
||||
if "direction" in damage_number:
|
||||
damage_number.direction = Vector2(0, -1)
|
||||
damage_number.move_duration = 1.0
|
||||
if "label" in damage_number:
|
||||
damage_number.label = "+1 COIN"
|
||||
if "color" in damage_number:
|
||||
damage_number.color = Color.YELLOW
|
||||
if "stats" in bodyToPickUp:
|
||||
bodyToPickUp.stats.add_coin(1)
|
||||
await $SfxCoinCollect.finished
|
||||
call_deferred("queue_free")
|
||||
return
|
||||
if is_collected:
|
||||
return
|
||||
if timeBeforePickup > 0.0:
|
||||
timeBeforePickup -= delta
|
||||
if timeBeforePickup < 0:
|
||||
$Area2DCollision.set_deferred("monitoring", true)
|
||||
timeBeforePickup = 0
|
||||
if bounceTimer > 0.0:
|
||||
bounceTimer -= delta
|
||||
if bounceTimer < 0:
|
||||
bounceTimer = 0
|
||||
|
||||
# Update vertical movement
|
||||
velocityZ += accelerationZ * delta
|
||||
positionZ += velocityZ * delta
|
||||
|
||||
# Ground collision and bounce
|
||||
if positionZ <= 0:
|
||||
velocity = velocity.lerp(Vector2.ZERO, 1.0 - exp(-friction * delta)) # only slow down if on floor
|
||||
positionZ = 0
|
||||
if abs(velocityZ) > minBounceVelocity:
|
||||
#print(velocityZ)
|
||||
$SfxCoinBounce.volume_db = -1 + (-10 - (velocityZ * 0.1))
|
||||
$SfxCoinBounce.play()
|
||||
velocityZ = -velocityZ * bounceRestitution
|
||||
else:
|
||||
velocityZ = 0
|
||||
|
||||
update_sprite_scale()
|
||||
move_and_slide()
|
||||
'
|
||||
func _physics_process(delta: float) -> void:
|
||||
if multiplayer.is_server():
|
||||
for peer_id in multiplayer.get_peers():
|
||||
sync_coin.rpc_id(peer_id, position, velocity, positionZ, velocityZ)
|
||||
if !multiplayer.is_server():
|
||||
position = position.lerp(sync_position, delta * 15.0)
|
||||
velocity = velocity.lerp(sync_velocity, delta * 15.0)
|
||||
positionZ = sync_positionZ
|
||||
velocityZ = sync_velocityZ
|
||||
if positionZ <= 0:
|
||||
velocity = velocity.lerp(Vector2.ZERO, 1.0 - exp(-friction * delta)) # only slow down if on floor
|
||||
positionZ = 0
|
||||
if abs(velocityZ) > minBounceVelocity:
|
||||
#print(velocityZ)
|
||||
$SfxCoinBounce.volume_db = -1 + (-10 - (velocityZ * 0.1))
|
||||
$SfxCoinBounce.play()
|
||||
|
||||
update_sprite_scale()
|
||||
pass
|
||||
'
|
||||
@rpc("unreliable")
|
||||
func sync_coin(pos: Vector2, vel: Vector2, posZ: float, velZ: float):
|
||||
sync_position = pos
|
||||
sync_velocity = vel
|
||||
sync_positionZ = posZ
|
||||
sync_velocityZ = velZ
|
||||
pass
|
||||
|
||||
func update_sprite_scale() -> void:
|
||||
# Calculate scale based on height
|
||||
# Maximum height will have scale 1.3, ground will have scale 1.0
|
||||
var height_factor = positionZ / 50.0 # Assuming 20 is max height
|
||||
var posY = 0.0 + (2 * positionZ)
|
||||
var sc = 1.0 + (0.8 * height_factor)
|
||||
$Sprite2D.scale = Vector2(sc, sc)
|
||||
$Sprite2D.position.y = -posY
|
||||
|
||||
func _on_area_2d_collision_body_entered(_body: Node2D) -> void:
|
||||
if bounceTimer == 0:
|
||||
$SfxCoinBounce.play()
|
||||
bounceTimer = 0.08
|
||||
# inverse the direction and slow down slightly
|
||||
var collision_shape = $Area2DCollision.get_overlapping_bodies()
|
||||
|
||||
if collision_shape.size() > 0:
|
||||
var collider = collision_shape[0]
|
||||
var normal = (global_position - collider.global_position).normalized()
|
||||
velocity = velocity.bounce(normal)
|
||||
|
||||
pass # Replace with function body.
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func pick_up(_char: CharacterBody2D):
|
||||
# does nothing... handled by coin itself!
|
||||
return false
|
||||
|
||||
func _on_area_2d_pickup_body_entered(body: Node2D) -> void:
|
||||
if !is_collected:
|
||||
bodyToPickUp = body.get_parent()
|
||||
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_pickup_body_exited(_body: Node2D) -> void:
|
||||
bodyToPickUp = null
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_pickup_area_entered(area: Area2D) -> void:
|
||||
if !is_collected:
|
||||
bodyToPickUp = area.get_parent()
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_pickup_area_exited(_area: Area2D) -> void:
|
||||
bodyToPickUp = null
|
||||
pass # Replace with function body.
|
||||
1
src/scripts/inspiration_scripts/coin.gd.uid
Normal file
1
src/scripts/inspiration_scripts/coin.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dxkd5t8jbbmdm
|
||||
617
src/scripts/inspiration_scripts/dungeon_generator.gd
Normal file
617
src/scripts/inspiration_scripts/dungeon_generator.gd
Normal file
@@ -0,0 +1,617 @@
|
||||
extends RefCounted # Using RefCounted instead of Node since this is a utility class
|
||||
|
||||
enum DUNGEON_ENTITY_TYPES {
|
||||
ENEMY,
|
||||
OBJECT,
|
||||
TRAP
|
||||
}
|
||||
|
||||
# Constants
|
||||
const DOOR_MODIFIERS = [
|
||||
{"name": "Locked Door", "type": "Locked"},
|
||||
{"name": "Bomb Wall", "type": "Bombable"}
|
||||
]
|
||||
|
||||
const FLOOR_TILES = [
|
||||
7,8,9,10,11,12,
|
||||
25,26,27,28,29,30,31,
|
||||
44,45,46,47,48,49,50,
|
||||
63,64,65,66,67,68,69
|
||||
]
|
||||
|
||||
const WALL_VARIATIONS = [
|
||||
0.45,
|
||||
0.15,
|
||||
0.15,
|
||||
0.15,
|
||||
0.1
|
||||
]
|
||||
|
||||
const OBJECT_TYPES = [
|
||||
{"name": "Barrel", "ti": [70], "ti2": [89], "openable": false, "liftable": true, "throwable": false, "hp": - 1, "pushable": true, "size": {"x": 1, "y": 1}},
|
||||
{"name": "Pot", "ti": [13, 14, 15], "ti2": [51, 52, 53], "openable": false, "liftable": true, "throwable": true, "hp": 1, "pushable": true, "size": {"x": 1, "y": 1}},
|
||||
{"name": "Chest", "ti": [108], "ti2": [127], "openable": true, "liftable": false, "throwable": false, "hp": - 1, "pushable": false, "size": {"x": 1, "y": 1}},
|
||||
{"name": "Bench", "ti": [35, 36], "ti2": [16, 17], "openable": false, "liftable": false, "throwable": false, "hp": - 1, "pushable": false, "size": {"x": 2, "y": 1}}
|
||||
]
|
||||
|
||||
const MONSTER_TYPES = [
|
||||
{
|
||||
"name": "Goblin",
|
||||
},
|
||||
{
|
||||
"name": "Slime",
|
||||
},
|
||||
# ... other monster types similar to JS version
|
||||
]
|
||||
|
||||
const TRAP_TYPES = [
|
||||
{"name": "Spike Trap", "description": "Spikes shoot up from the floor when triggered."},
|
||||
{"name": "Arrow Trap", "description": "Arrows fire from the walls when a player steps on a pressure plate."},
|
||||
# ... other trap types
|
||||
]
|
||||
|
||||
const ROOM_MODIFIERS = [
|
||||
{"name": "Player Start", "description": "The players start here.", "type": "START", "negative_modifiers": {"min": 0, "max": 0}},
|
||||
{"name": "Exit", "description": "Room contains an exit.", "type": "EXIT", "negative_modifiers": {"min": 0, "max": 0}},
|
||||
# ... other room modifiers
|
||||
]
|
||||
|
||||
# Main generation function
|
||||
func generate_dungeon(map_size: Vector2, _num_rooms: int, min_room_size: int, max_room_size: int) -> Dictionary:
|
||||
# Initialize grid
|
||||
var grid = []
|
||||
var randgrid = []
|
||||
for x in range(map_size.x):
|
||||
grid.append([])
|
||||
randgrid.append([])
|
||||
for y in range(map_size.y):
|
||||
grid[x].append(0)
|
||||
randgrid[x].append(0)
|
||||
|
||||
var all_rooms = []
|
||||
var all_doors = []
|
||||
|
||||
# 1. Create first room at a random position
|
||||
var first_w = rand_range_i(min_room_size, max_room_size)
|
||||
var first_h = rand_range_i(min_room_size, max_room_size)
|
||||
var first_room = {
|
||||
"x": rand_range_i(4, map_size.x - first_w - 4), # Random position with buffer
|
||||
"y": rand_range_i(4, map_size.y - first_h - 4),
|
||||
"w": first_w,
|
||||
"h": first_h,
|
||||
"modifiers": []
|
||||
}
|
||||
|
||||
set_floor(first_room, grid, map_size)
|
||||
all_rooms.append(first_room)
|
||||
|
||||
var nrOfDoorErrors = 0
|
||||
var nrOfRoomErrors = 0
|
||||
|
||||
# 2. Try to place rooms until we can't fit any more
|
||||
var attempts = 1000 # Prevent infinite loops
|
||||
while attempts > 0 and all_rooms.size() > 0:
|
||||
# Pick a random existing room
|
||||
var source_room = all_rooms[randi() % all_rooms.size()]
|
||||
|
||||
# Try to place a new room near it
|
||||
var new_room = try_place_room_near(source_room, grid, map_size, min_room_size, max_room_size)
|
||||
if new_room.w > 0: # Valid room created
|
||||
set_floor(new_room, grid, map_size)
|
||||
all_rooms.append(new_room)
|
||||
|
||||
attempts -= 1
|
||||
if attempts <= 0:
|
||||
nrOfRoomErrors += 1
|
||||
break
|
||||
|
||||
# 3. Connect rooms with corridors/doors
|
||||
if all_rooms.size() > 1:
|
||||
var connected_rooms = {}
|
||||
for room in all_rooms:
|
||||
connected_rooms[room] = []
|
||||
|
||||
# First pass: try to connect each room to its closest neighbors
|
||||
for room in all_rooms:
|
||||
var closest_rooms = find_closest_rooms(room, all_rooms)
|
||||
#print("Connecting room at ", room.x, ",", room.y)
|
||||
|
||||
var connection_attempts = 0
|
||||
var max_connection_attempts = 3 # Try to connect to multiple neighbors
|
||||
|
||||
for target_room in closest_rooms:
|
||||
if connection_attempts >= max_connection_attempts:
|
||||
break
|
||||
|
||||
if not rooms_are_connected(room, target_room, all_doors):
|
||||
var door = create_corridor_between_rooms(room, target_room, grid)
|
||||
if door.size() > 0:
|
||||
#print("Created direct connection between rooms")
|
||||
set_door(door, grid)
|
||||
all_doors.append(door)
|
||||
connected_rooms[room].append(target_room)
|
||||
connected_rooms[target_room].append(room)
|
||||
connection_attempts += 1
|
||||
|
||||
# Second pass: ensure all rooms are connected
|
||||
var attempts2 = 100
|
||||
while attempts2 > 0:
|
||||
var reachable = find_reachable_rooms(all_rooms[0], all_rooms, all_doors)
|
||||
#print("Reachable rooms: ", reachable.size(), "/", all_rooms.size())
|
||||
|
||||
if reachable.size() == all_rooms.size():
|
||||
#print("All rooms connected!")
|
||||
break
|
||||
|
||||
# Find an unreachable room and try to connect it
|
||||
for room in all_rooms:
|
||||
if not reachable.has(room):
|
||||
var connected = false
|
||||
|
||||
# Try to connect to each reachable room until success
|
||||
for target_room in reachable:
|
||||
var door = create_corridor_between_rooms(room, target_room, grid)
|
||||
if door.size() > 0:
|
||||
set_door(door, grid)
|
||||
all_doors.append(door)
|
||||
connected = true
|
||||
break
|
||||
|
||||
if not connected:
|
||||
# Try creating intermediate room with multiple positions
|
||||
for offset_x in [-2, 0, 2]:
|
||||
for offset_y in [-2, 0, 2]:
|
||||
var mid_room = create_intermediate_room(room, reachable[0], offset_x, offset_y)
|
||||
if is_valid_room_position(mid_room, grid, map_size):
|
||||
set_floor(mid_room, grid, map_size)
|
||||
all_rooms.append(mid_room)
|
||||
|
||||
var door1 = create_corridor_between_rooms(room, mid_room, grid)
|
||||
var door2 = create_corridor_between_rooms(mid_room, reachable[0], grid)
|
||||
|
||||
if door1.size() > 0 and door2.size() > 0:
|
||||
set_door(door1, grid)
|
||||
set_door(door2, grid)
|
||||
all_doors.append(door1)
|
||||
all_doors.append(door2)
|
||||
connected = true
|
||||
break
|
||||
if connected:
|
||||
break
|
||||
|
||||
if connected:
|
||||
break
|
||||
|
||||
attempts2 -= 1
|
||||
if attempts2 <= 0:
|
||||
nrOfDoorErrors += 1
|
||||
break
|
||||
|
||||
for x in range(map_size.x):
|
||||
for y in range(map_size.y):
|
||||
if grid[x][y] == 0: # wall
|
||||
var rand = randf()
|
||||
var sum:float = 0.0
|
||||
for i in WALL_VARIATIONS.size():
|
||||
sum += WALL_VARIATIONS[i];
|
||||
if rand <= sum:
|
||||
randgrid[x][y] = i
|
||||
break
|
||||
elif grid[x][y] == 1: # floor
|
||||
if randf() < 0.6:
|
||||
randgrid[x][y] = 0
|
||||
else:
|
||||
randgrid[x][y] = randi_range(1,FLOOR_TILES.size()-1)
|
||||
elif grid[x][y] == 2: # door
|
||||
randgrid[x][y] = 0 # we dont care about these... only have 1 variant
|
||||
|
||||
var startRoomIndex = randi_range(0,all_rooms.size()-1)
|
||||
all_rooms[startRoomIndex].modifiers.push_back(ROOM_MODIFIERS[0])
|
||||
|
||||
var farthestRoom = null
|
||||
var maxDistance = 0
|
||||
var exitRoomIndex = -1
|
||||
var roomIndex = 0
|
||||
for r in all_rooms:
|
||||
var distance = abs(r.x - all_rooms[startRoomIndex].x) + abs(r.y - all_rooms[startRoomIndex].y)
|
||||
if (distance > maxDistance):
|
||||
maxDistance = distance
|
||||
farthestRoom = r
|
||||
exitRoomIndex = roomIndex
|
||||
roomIndex+=1
|
||||
pass
|
||||
|
||||
farthestRoom.modifiers.push_back(ROOM_MODIFIERS[1])
|
||||
|
||||
var entities = []
|
||||
var TILE_SIZE = 16
|
||||
|
||||
roomIndex = 0
|
||||
#populate rooms and decide modifiers for rooms
|
||||
for r in all_rooms:
|
||||
if roomIndex != startRoomIndex and roomIndex != exitRoomIndex:
|
||||
var validRoomLocations = []
|
||||
var min_x = (r.x + 1)
|
||||
var max_x = ((r.x + r.w) - 1)
|
||||
var min_y = (r.y + 1)
|
||||
var max_y = ((r.y + r.h) - 1)
|
||||
for rw in range(min_x,max_x):
|
||||
for rh in range(min_y, max_y):
|
||||
validRoomLocations.push_back(Vector2(rw*TILE_SIZE - 8, rh*TILE_SIZE - 8)) # we assume entities are 16x16 are centered
|
||||
# bigger rooms can have a larger content number!
|
||||
var randNrOfEntities = randi_range(0, 4)
|
||||
|
||||
for entI in randNrOfEntities:
|
||||
|
||||
var enttype:DUNGEON_ENTITY_TYPES = randi_range(0, DUNGEON_ENTITY_TYPES.size()-1) as DUNGEON_ENTITY_TYPES
|
||||
var entStats = {}
|
||||
# hand code to only be enemies atm
|
||||
if enttype == DUNGEON_ENTITY_TYPES.TRAP:
|
||||
enttype = DUNGEON_ENTITY_TYPES.OBJECT
|
||||
#enttype = DUNGEON_ENTITY_TYPES.OBJECT ## only objects now...
|
||||
var subtype = "goblin"
|
||||
if enttype == DUNGEON_ENTITY_TYPES.ENEMY:
|
||||
var randType = randi_range(0, 1)
|
||||
var cStats = CharacterStats.new()
|
||||
if randType == 1:
|
||||
cStats.hp = 2
|
||||
subtype = "slime"
|
||||
else:
|
||||
cStats.hp = 3
|
||||
cStats.skin = "res://assets/gfx/Puny-Characters/Layer 0 - Skins/Orc1.png"
|
||||
cStats.skin = "res://assets/gfx/Puny-Characters/Layer 0 - Skins/Orc2.png"
|
||||
|
||||
var hair = 0
|
||||
if randf() > 0.6:
|
||||
hair = randi_range(1,13)
|
||||
cStats.setHair(hair, randi_range(0,8))
|
||||
var facialhair = 0
|
||||
if randf() > 0.75: # very uncommon for facial hair on goblins
|
||||
facialhair = randi_range(1,3)
|
||||
cStats.setFacialHair(facialhair, randi_range(0, 4))
|
||||
|
||||
#cStats.add_on = "res://assets/gfx/Puny-Characters/Layer 7 - Add-ons/Orc Add-ons/GoblinEars1.png"
|
||||
cStats.add_on = "res://assets/gfx/Puny-Characters/Layer 7 - Add-ons/Orc Add-ons/GoblinEars2.png"
|
||||
#cStats.add_on = "res://assets/gfx/Puny-Characters/Layer 7 - Add-ons/Orc Add-ons/OrcJaw1.png"
|
||||
#cStats.add_on = "res://assets/gfx/Puny-Characters/Layer 7 - Add-ons/Orc Add-ons/OrcJaw2.png"
|
||||
# randomize if the goblin will have a weapon like dagger or sword
|
||||
# randomize if the goblin will have bow and arrows also
|
||||
# randomize if the goblin will have an armour and helmet etc.
|
||||
|
||||
entStats = cStats.save()
|
||||
elif enttype == DUNGEON_ENTITY_TYPES.OBJECT:
|
||||
subtype = "pot"
|
||||
else:
|
||||
subtype = "spike"
|
||||
|
||||
var posI = randi_range(0, validRoomLocations.size()-1)
|
||||
|
||||
var entity = {
|
||||
"type": enttype,
|
||||
"subtype": subtype,
|
||||
"stats": entStats,
|
||||
"position": {
|
||||
"x": validRoomLocations[posI].x,
|
||||
"y": validRoomLocations[posI].y
|
||||
}
|
||||
}
|
||||
entities.push_back(entity)
|
||||
validRoomLocations.remove_at(posI) # this is now occupied... don't allow anything else spawn on it.
|
||||
|
||||
# fill up modifiers per room
|
||||
if ROOM_MODIFIERS.size() > 2:
|
||||
r.modifiers.push_back(ROOM_MODIFIERS[randi_range(2, ROOM_MODIFIERS.size()-2)])
|
||||
pass
|
||||
roomIndex += 1
|
||||
|
||||
|
||||
|
||||
return {
|
||||
"rooms": all_rooms,
|
||||
"entities": entities,
|
||||
"doors": all_doors,
|
||||
"grid": grid,
|
||||
"randgrid": randgrid, # grid containing actual tile index-ish(ish)
|
||||
"mapSize": map_size,
|
||||
"nrOfDoorErrors": nrOfDoorErrors,
|
||||
"nrOfRoomErrors": nrOfRoomErrors
|
||||
}
|
||||
|
||||
# Helper functions
|
||||
func create_random_room(map_size: Vector2, min_size: int, max_size: int) -> Dictionary:
|
||||
var x = randi() % (int(map_size.x) - max_size - 2) + 1
|
||||
var y = randi() % (int(map_size.y) - max_size - 2) + 1
|
||||
var w = rand_range_i(min_size, max_size)
|
||||
var h = rand_range_i(min_size, max_size)
|
||||
return {"x": x, "y": y, "w": w, "h": h, "modifiers": []}
|
||||
|
||||
func rand_range_i(min_val: int, max_val: int) -> int:
|
||||
return min_val + (randi() % (max_val - min_val + 1))
|
||||
|
||||
func set_floor(room: Dictionary, grid: Array, map_size: Vector2) -> void:
|
||||
for x in range(room.x, room.x + room.w):
|
||||
for y in range(room.y, room.y + room.h):
|
||||
if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
|
||||
grid[x][y] = 1 # Set as floor tile
|
||||
|
||||
# ... Additional helper functions and implementation details would follow ...
|
||||
# ... previous code ...
|
||||
|
||||
func try_place_room_near(source_room: Dictionary, grid: Array, map_size: Vector2,
|
||||
min_room_size: int, max_room_size: int) -> Dictionary:
|
||||
var attempts = 20
|
||||
while attempts > 0:
|
||||
var w = rand_range_i(min_room_size, max_room_size)
|
||||
var h = rand_range_i(min_room_size, max_room_size)
|
||||
|
||||
# Try all four sides of the source room
|
||||
var sides = ["N", "S", "E", "W"]
|
||||
sides.shuffle()
|
||||
|
||||
for side in sides:
|
||||
var x = source_room.x
|
||||
var y = source_room.y
|
||||
|
||||
match side:
|
||||
"N":
|
||||
x = source_room.x + (randi() % max(1, source_room.w - w))
|
||||
y = source_room.y - h - 4 # 4 tiles away
|
||||
"S":
|
||||
x = source_room.x + (randi() % max(1, source_room.w - w))
|
||||
y = source_room.y + source_room.h + 4
|
||||
"W":
|
||||
x = source_room.x - w - 4
|
||||
y = source_room.y + (randi() % max(1, source_room.h - h))
|
||||
"E":
|
||||
x = source_room.x + source_room.w + 4
|
||||
y = source_room.y + (randi() % max(1, source_room.h - h))
|
||||
|
||||
if is_valid_room_position({"x": x, "y": y, "w": w, "h": h}, grid, map_size):
|
||||
return {"x": x, "y": y, "w": w, "h": h, "modifiers": []}
|
||||
|
||||
attempts -= 1
|
||||
|
||||
return {"x": 0, "y": 0, "w": 0, "h": 0, "modifiers": []}
|
||||
|
||||
func find_closest_rooms(room: Dictionary, all_rooms: Array) -> Array:
|
||||
if all_rooms.size() <= 1:
|
||||
return []
|
||||
|
||||
var distances = []
|
||||
for other in all_rooms:
|
||||
if other == room:
|
||||
continue
|
||||
var dist = abs(room.x - other.x) + abs(room.y - other.y)
|
||||
distances.append({"room": other, "distance": dist})
|
||||
|
||||
# Sort by distance
|
||||
if distances.size() > 0:
|
||||
distances.sort_custom(func(a, b): return a.distance < b.distance)
|
||||
# Return the rooms only, in order of distance
|
||||
return distances.map(func(item): return item.room)
|
||||
|
||||
return []
|
||||
|
||||
func rooms_are_connected(room1: Dictionary, room2: Dictionary, doors: Array) -> bool:
|
||||
for door in doors:
|
||||
if (door.room1 == room1 and door.room2 == room2) or \
|
||||
(door.room1 == room2 and door.room2 == room1):
|
||||
return true
|
||||
return false
|
||||
|
||||
func create_corridor_between_rooms(room1: Dictionary, room2: Dictionary, _grid: Array) -> Dictionary:
|
||||
# Determine if rooms are more horizontal or vertical from each other
|
||||
var dx = abs(room2.x - room1.x)
|
||||
var dy = abs(room2.y - room1.y)
|
||||
|
||||
# Check if rooms are too far apart (more than 8 tiles)
|
||||
if dx > 8 and dy > 8:
|
||||
return {}
|
||||
|
||||
if dx > dy:
|
||||
# Horizontal corridor
|
||||
var leftRoom = room1 if room1.x < room2.x else room2
|
||||
var rightRoom = room2 if room1.x < room2.x else room1
|
||||
|
||||
# Check if rooms are horizontally adjacent (gap should be reasonable)
|
||||
if rightRoom.x - (leftRoom.x + leftRoom.w) > 8:
|
||||
return {}
|
||||
|
||||
# Door must start at the right edge of left room plus 1 tile gap
|
||||
var door_x = leftRoom.x + leftRoom.w
|
||||
|
||||
# Door y must be within both rooms' height ranges, accounting for walls
|
||||
var min_y = max(leftRoom.y + 1, rightRoom.y + 1) # +1 to account for walls
|
||||
var max_y = min(leftRoom.y + leftRoom.h - 2, rightRoom.y + rightRoom.h - 2) # -2 to ensure both tiles fit
|
||||
|
||||
# Make sure we have a valid range
|
||||
if max_y < min_y:
|
||||
return {}
|
||||
|
||||
# Pick a valid y position within the range
|
||||
var door_y = min_y + (randi() % max(1, max_y - min_y + 1))
|
||||
|
||||
# Calculate actual width needed (distance between rooms)
|
||||
var door_width = rightRoom.x - (leftRoom.x + leftRoom.w + 1)
|
||||
# Use the larger of minimum width (4) or actual distance
|
||||
door_width = max(4, door_width + 1)
|
||||
|
||||
# Create door with calculated width
|
||||
var door = {
|
||||
"x": door_x,
|
||||
"y": door_y,
|
||||
"w": door_width, # Use calculated width
|
||||
"h": 2, # Fixed height for horizontal doors
|
||||
"dir": "E" if leftRoom == room1 else "W",
|
||||
"room1": room1,
|
||||
"room2": room2
|
||||
}
|
||||
|
||||
return door
|
||||
else:
|
||||
# Vertical corridor
|
||||
var topRoom = room1 if room1.y < room2.y else room2
|
||||
var bottomRoom = room2 if room1.y < room2.y else room1
|
||||
|
||||
# Check if rooms are vertically adjacent (gap should be reasonable)
|
||||
if bottomRoom.y - (topRoom.y + topRoom.h) > 8:
|
||||
return {}
|
||||
|
||||
# Door must start at the bottom edge of top room plus 1 tile gap
|
||||
var door_y = topRoom.y + topRoom.h
|
||||
|
||||
# Door x must be within both rooms' width ranges, accounting for walls
|
||||
var min_x = max(topRoom.x + 1, bottomRoom.x + 1) # +1 to account for walls
|
||||
var max_x = min(topRoom.x + topRoom.w - 2, bottomRoom.x + bottomRoom.w - 2) # -2 to ensure both tiles fit
|
||||
|
||||
# Make sure we have a valid range
|
||||
if max_x < min_x:
|
||||
return {}
|
||||
|
||||
# Pick a valid x position within the range
|
||||
var door_x = min_x + (randi() % max(1, max_x - min_x + 1))
|
||||
|
||||
# Calculate actual height needed (distance between rooms)
|
||||
var door_height = bottomRoom.y - (topRoom.y + topRoom.h + 1)
|
||||
# Use the larger of minimum height (4) or actual distance
|
||||
door_height = max(4, door_height + 1)
|
||||
|
||||
# Create door with calculated height
|
||||
var door = {
|
||||
"x": door_x,
|
||||
"y": door_y,
|
||||
"w": 2, # Fixed width for vertical doors
|
||||
"h": door_height, # Use calculated height
|
||||
"dir": "S" if topRoom == room1 else "N",
|
||||
"room1": room1,
|
||||
"room2": room2
|
||||
}
|
||||
|
||||
return door
|
||||
|
||||
|
||||
func add_room_modifiers(rooms: Array) -> void:
|
||||
# Add start room modifier to first room
|
||||
rooms[0].modifiers.append(ROOM_MODIFIERS[0]) # START modifier
|
||||
|
||||
# Add exit to last room
|
||||
rooms[-1].modifiers.append(ROOM_MODIFIERS[1]) # EXIT modifier
|
||||
|
||||
# Add random modifiers to other rooms
|
||||
for i in range(1, rooms.size() - 1):
|
||||
if randf() < 0.3: # 30% chance for a modifier
|
||||
var available_modifiers = ROOM_MODIFIERS.slice(2, ROOM_MODIFIERS.size())
|
||||
# Only add modifier if there are available ones
|
||||
if available_modifiers.size() > 0:
|
||||
rooms[i].modifiers.append(available_modifiers[randi() % available_modifiers.size()])
|
||||
|
||||
func generate_all_room_objects(rooms: Array, doors: Array) -> Array:
|
||||
var room_objects = []
|
||||
|
||||
# Generate objects for each room
|
||||
for room in rooms:
|
||||
var objects = generate_room_objects(room)
|
||||
room_objects.append_array(objects)
|
||||
|
||||
# Add door modifiers
|
||||
for door in doors:
|
||||
if randf() < 0.2: # 20% chance for door modifier
|
||||
var modifier = DOOR_MODIFIERS[randi() % DOOR_MODIFIERS.size()]
|
||||
room_objects.append({
|
||||
"type": "Door",
|
||||
"x": door.x,
|
||||
"y": door.y,
|
||||
"modifier": modifier
|
||||
})
|
||||
|
||||
return room_objects
|
||||
|
||||
func generate_room_objects(room: Dictionary) -> Array:
|
||||
var objects = []
|
||||
var max_objects = int((room.w * room.h) / 16) # Roughly one object per 16 tiles
|
||||
|
||||
for _i in range(max_objects):
|
||||
if randf() < 0.7: # 70% chance to place each potential object
|
||||
var obj_type = OBJECT_TYPES[randi() % OBJECT_TYPES.size()]
|
||||
var x = rand_range_i(room.x + 1, room.x + room.w - obj_type.size.x - 1)
|
||||
var y = rand_range_i(room.y + 1, room.y + room.h - obj_type.size.y - 1)
|
||||
|
||||
# Check if position is free
|
||||
var can_place = true
|
||||
for obj in objects:
|
||||
if abs(obj.x - x) < 2 and abs(obj.y - y) < 2:
|
||||
can_place = false
|
||||
break
|
||||
|
||||
if can_place:
|
||||
objects.append({
|
||||
"type": obj_type.name,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"properties": obj_type
|
||||
})
|
||||
|
||||
return objects
|
||||
|
||||
func set_door(door: Dictionary, grid: Array) -> void:
|
||||
match door.dir:
|
||||
"N", "S":
|
||||
if door.h > 4:
|
||||
# Set 2x4 corridor
|
||||
for dx in range(2):
|
||||
for dy in range(door.h):
|
||||
#if grid[door.x + dx][door.y + dy] != 1: # Don't overwrite room
|
||||
grid[door.x + dx][door.y + dy] = 2
|
||||
"E", "W":
|
||||
if door.w > 4:
|
||||
# Set 4x2 corridor
|
||||
for dx in range(door.w):
|
||||
for dy in range(2):
|
||||
#if grid[door.x + dx][door.y + dy] != 1: # Don't overwrite room
|
||||
grid[door.x + dx][door.y + dy] = 2
|
||||
|
||||
func is_valid_room_position(room: Dictionary, grid: Array, map_size: Vector2) -> bool:
|
||||
# Check if room is within map bounds with buffer
|
||||
if room.x < 4 or room.y < 4 or room.x + room.w >= map_size.x - 4 or room.y + room.h >= map_size.y - 4:
|
||||
#print("Room outside map bounds")
|
||||
return false
|
||||
|
||||
# Check if room overlaps with existing rooms or corridors
|
||||
# Check the actual room area plus 4-tile buffer for spacing
|
||||
for x in range(room.x - 4, room.x + room.w + 4):
|
||||
for y in range(room.y - 4, room.y + room.h + 4):
|
||||
if x >= 0 and x < map_size.x and y >= 0 and y < map_size.y:
|
||||
if grid[x][y] != 0: # If tile is not empty
|
||||
#print("Room overlaps at ", Vector2(x, y))
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
# Add this helper function to check room connectivity
|
||||
func find_reachable_rooms(start_room: Dictionary, _all_rooms: Array, all_doors: Array) -> Array:
|
||||
var reachable = [start_room]
|
||||
var queue = [start_room]
|
||||
|
||||
while queue.size() > 0:
|
||||
var current = queue.pop_front()
|
||||
for door in all_doors:
|
||||
var next_room = null
|
||||
if door.room1 == current:
|
||||
next_room = door.room2
|
||||
elif door.room2 == current:
|
||||
next_room = door.room1
|
||||
|
||||
if next_room != null and not reachable.has(next_room):
|
||||
reachable.append(next_room)
|
||||
queue.append(next_room)
|
||||
|
||||
return reachable
|
||||
|
||||
func create_intermediate_room(room1: Dictionary, room2: Dictionary, offset_x: int, offset_y: int) -> Dictionary:
|
||||
return {
|
||||
"x": room1.x + (room2.x - room1.x) / 2 + offset_x,
|
||||
"y": room1.y + (room2.y - room1.y) / 2 + offset_y,
|
||||
"w": 6,
|
||||
"h": 6,
|
||||
"modifiers": []
|
||||
}
|
||||
1
src/scripts/inspiration_scripts/dungeon_generator.gd.uid
Normal file
1
src/scripts/inspiration_scripts/dungeon_generator.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dj0n3aevweyhu
|
||||
116
src/scripts/inspiration_scripts/item.gd
Normal file
116
src/scripts/inspiration_scripts/item.gd
Normal file
@@ -0,0 +1,116 @@
|
||||
class_name Item
|
||||
|
||||
func _init(iDic: Dictionary = {}):
|
||||
if iDic != {}:
|
||||
self.load(iDic)
|
||||
|
||||
enum ItemType {
|
||||
Restoration,
|
||||
Equippable,
|
||||
Throwable,
|
||||
Bomb,
|
||||
}
|
||||
|
||||
# only relevant for equipment
|
||||
enum EquipmentType {
|
||||
NONE,
|
||||
MAINHAND,
|
||||
OFFHAND,
|
||||
HEADGEAR,
|
||||
ARMOUR,
|
||||
BOOTS,
|
||||
ACCESSORY
|
||||
}
|
||||
|
||||
enum WeaponType {
|
||||
NONE,
|
||||
AMMUNITION,
|
||||
BOW,
|
||||
SWORD,
|
||||
AXE
|
||||
}
|
||||
|
||||
var use_function = null
|
||||
|
||||
var item_name: String = "Red Apple"
|
||||
var description: String = "Restores 5 HP"
|
||||
var spritePath: String = "res://assets/gfx/items_n_shit.png"
|
||||
var equipmentPath: String = "res://assets/gfx/Puny-Characters/Layer 2 - Clothes/Basic Body/BasicRed.png"
|
||||
var colorReplacements: Array = []
|
||||
var spriteFrames:Vector2i = Vector2i(20,14)
|
||||
var spriteFrame:int = 0
|
||||
var modifiers: Dictionary = { "hp": +20 }
|
||||
var duration: float = 0
|
||||
|
||||
var buy_cost: int = 10
|
||||
var sell_worth: int = 3
|
||||
var sellable:bool = true
|
||||
var item_type: ItemType = ItemType.Restoration
|
||||
var equipment_type: EquipmentType = EquipmentType.NONE
|
||||
var weapon_type: WeaponType = WeaponType.NONE
|
||||
var two_handed:bool = false
|
||||
var quantity = 1
|
||||
var can_have_multiple_of:bool = false
|
||||
|
||||
func save():
|
||||
var json = {
|
||||
"item_name": item_name,
|
||||
"description": description,
|
||||
"spritePath": spritePath,
|
||||
"equipmentPath": equipmentPath,
|
||||
"spriteFrame": spriteFrame,
|
||||
"modifiers": modifiers,
|
||||
"duration": duration,
|
||||
"buy_cost": buy_cost,
|
||||
"sell_worth": sell_worth,
|
||||
"item_type": item_type,
|
||||
"equipment_type": equipment_type,
|
||||
"weapon_type": weapon_type,
|
||||
"two_handed": two_handed,
|
||||
"quantity": quantity,
|
||||
"can_have_multiple_of": can_have_multiple_of
|
||||
}
|
||||
return json
|
||||
|
||||
func from_dict(iDic: Dictionary) -> Item:
|
||||
self.load(iDic)
|
||||
return self
|
||||
|
||||
func load(iDic: Dictionary):
|
||||
if iDic.has("item_name"):
|
||||
item_name = iDic.get("item_name")
|
||||
if iDic.has("description"):
|
||||
description = iDic.get("description")
|
||||
if iDic.has("spritePath"):
|
||||
spritePath = iDic.get("spritePath")
|
||||
if iDic.has("equipmentPath"):
|
||||
equipmentPath = iDic.get("equipmentPath")
|
||||
|
||||
#if iDic.has("spriteFrames"):
|
||||
#spriteFrames = iDic.get("spriteFrames")
|
||||
|
||||
if iDic.has("spriteFrame"):
|
||||
spriteFrame = iDic.get("spriteFrame")
|
||||
if iDic.has("modifiers"):
|
||||
modifiers = iDic.get("modifiers")
|
||||
|
||||
if iDic.has("duration"):
|
||||
duration = iDic.get("duration")
|
||||
|
||||
if iDic.has("buy_cost"):
|
||||
buy_cost = iDic.get("buy_cost")
|
||||
if iDic.has("sell_worth"):
|
||||
sell_worth = iDic.get("sell_worth")
|
||||
if iDic.has("item_type"):
|
||||
item_type = iDic.get("item_type")
|
||||
if iDic.has("equipment_type"):
|
||||
equipment_type = iDic.get("equipment_type")
|
||||
if iDic.has("weapon_type"):
|
||||
weapon_type = iDic.get("weapon_type")
|
||||
if iDic.has("two_handed"):
|
||||
two_handed = iDic.get("two_handed")
|
||||
if iDic.has("quantity"):
|
||||
quantity = iDic.get("quantity")
|
||||
if iDic.has("can_have_multiple_of"):
|
||||
can_have_multiple_of = iDic.get("can_have_multiple_of")
|
||||
pass
|
||||
1
src/scripts/inspiration_scripts/item.gd.uid
Normal file
1
src/scripts/inspiration_scripts/item.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1nl6a63n5wtr
|
||||
167
src/scripts/inspiration_scripts/loot.gd
Normal file
167
src/scripts/inspiration_scripts/loot.gd
Normal file
@@ -0,0 +1,167 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
var it: Item = null
|
||||
@export var sync_has_been_picked_up: bool = false
|
||||
|
||||
var friction = 8.0 # Lower values make the slowdown more gradual
|
||||
var bounceTimer = 0.0
|
||||
# Z-axis variables
|
||||
var positionZ = 4.0 # Start slightly in the air
|
||||
var velocityZ = 10.0 # Initial upward velocity
|
||||
var accelerationZ = -330.0 # Gravity
|
||||
var bounceRestitution = 0.3 # How much bounce energy is retained (0-1)
|
||||
var minBounceVelocity = 60.0 # Minimum velocity needed to bounce
|
||||
|
||||
var sync_position := Vector2()
|
||||
var sync_velocity := Vector2()
|
||||
var sync_positionZ: float = 4.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("loot")
|
||||
|
||||
# Initialize item if needed
|
||||
if it == null:
|
||||
setItem()
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if multiplayer.is_server():
|
||||
if bounceTimer > 0.0:
|
||||
bounceTimer -= delta
|
||||
if bounceTimer < 0:
|
||||
bounceTimer = 0
|
||||
|
||||
# Update vertical movement
|
||||
velocityZ += accelerationZ * delta
|
||||
positionZ += velocityZ * delta
|
||||
|
||||
# Ground collision and bounce
|
||||
if positionZ <= 0:
|
||||
velocity = velocity.lerp(Vector2.ZERO, 1.0 - exp(-friction * delta)) # only slow down if on floor
|
||||
positionZ = 0
|
||||
if abs(velocityZ) > minBounceVelocity:
|
||||
#Bounce sound here for items:
|
||||
#$SfxCoinBounce.volume_db = -1 + (-10-(velocityZ*0.1))
|
||||
#$SfxCoinBounce.play()
|
||||
velocityZ = -velocityZ * bounceRestitution
|
||||
else:
|
||||
velocityZ = 0
|
||||
|
||||
update_sprite_scale()
|
||||
move_and_slide()
|
||||
pass
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if multiplayer.is_server():
|
||||
for peer_id in multiplayer.get_peers():
|
||||
sync_loot.rpc_id(peer_id, position, velocity, positionZ)
|
||||
if !multiplayer.is_server():
|
||||
position = sync_position
|
||||
velocity = sync_velocity
|
||||
positionZ = sync_positionZ
|
||||
update_sprite_scale()
|
||||
pass
|
||||
|
||||
@rpc("unreliable")
|
||||
func sync_loot(pos: Vector2, vel: Vector2, posZ: float):
|
||||
sync_position = pos
|
||||
sync_velocity = vel
|
||||
sync_positionZ = posZ
|
||||
pass
|
||||
|
||||
func update_sprite_scale() -> void:
|
||||
# Calculate scale based on height
|
||||
# Maximum height will have scale 1.3, ground will have scale 1.0
|
||||
var height_factor = positionZ / 50.0 # Assuming 20 is max height
|
||||
var posY = 0.0 + (2 * positionZ)
|
||||
var sc = 1.0 + (0.8 * height_factor)
|
||||
$Sprite2D.scale = Vector2(sc, sc)
|
||||
$Sprite2D.position.y = -posY
|
||||
pass
|
||||
|
||||
func setItem(iItem: Item = null) -> void:
|
||||
$Label.visible = false
|
||||
if iItem != null:
|
||||
it = iItem
|
||||
else:
|
||||
it = Item.new()
|
||||
if $Sprite2D.texture != null or $Sprite2D.texture.resource_path != it.spritePath:
|
||||
$Sprite2D.texture = load(it.spritePath)
|
||||
$Sprite2D.frame = it.spriteFrame
|
||||
pass
|
||||
|
||||
@rpc("any_peer", "reliable", "call_local")
|
||||
func request_pickup(_player_id: int):
|
||||
if multiplayer.is_server():
|
||||
sync_has_been_picked_up = true
|
||||
visible = false
|
||||
# Tell all clients to remove the loot
|
||||
remove_loot.rpc()
|
||||
$SfxPickup.play()
|
||||
await $SfxPickup.finished
|
||||
# Remove loot on server too
|
||||
queue_free()
|
||||
|
||||
# Called locally by the player picking up the item
|
||||
func pick_up_local(player_id: int):
|
||||
if sync_has_been_picked_up:
|
||||
return
|
||||
var player = MultiplayerManager.playersNode.get_node(str(player_id))
|
||||
if player:
|
||||
sync_has_been_picked_up = true
|
||||
visible = false
|
||||
# Add item to local inventory
|
||||
player.stats.add_item(it)
|
||||
# Tell server about pickup
|
||||
if not multiplayer.is_server():
|
||||
request_pickup.rpc_id(1, player_id)
|
||||
else:
|
||||
# Tell all clients to remove the loot
|
||||
remove_loot.rpc()
|
||||
$SfxPickup.play()
|
||||
await $SfxPickup.finished
|
||||
# Remove loot locally immediately
|
||||
queue_free()
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func remove_loot():
|
||||
visible = false
|
||||
sync_has_been_picked_up = true
|
||||
|
||||
$SfxPickup.play()
|
||||
await $SfxPickup.finished
|
||||
queue_free()
|
||||
|
||||
func _on_area_2d_body_entered(_body: Node2D) -> void:
|
||||
$Label.visible = true
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_body_exited(_body: Node2D) -> void:
|
||||
$Label.visible = false
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_collision_body_entered(_body: Node2D) -> void:
|
||||
if bounceTimer == 0:
|
||||
#$SfxCoinBounce.play()
|
||||
bounceTimer = 0.08
|
||||
# inverse the direction and slow down slightly
|
||||
var collision_shape = $Area2DCollision.get_overlapping_bodies()
|
||||
|
||||
if collision_shape.size() > 0:
|
||||
var collider = collision_shape[0]
|
||||
var normal = (global_position - collider.global_position).normalized()
|
||||
velocity = velocity.bounce(normal)
|
||||
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_pickup_area_entered(_area: Area2D) -> void:
|
||||
$Label.visible = true
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_area_2d_pickup_area_exited(_area: Area2D) -> void:
|
||||
$Label.visible = false
|
||||
pass # Replace with function body.
|
||||
1
src/scripts/inspiration_scripts/loot.gd.uid
Normal file
1
src/scripts/inspiration_scripts/loot.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bawxh5vhj4ii3
|
||||
Reference in New Issue
Block a user