Compare commits
3 commits
71c02b362a
...
e595e9d0e9
Author | SHA1 | Date | |
---|---|---|---|
e595e9d0e9 | |||
8205b91ec9 | |||
ee5273b501 |
97 changed files with 6 additions and 15203 deletions
Cargo.toml
character_window
Cargo.toml
resources
abilities
ability_tooltip.xmladdon_tooltip.xmladdon_type_snippet.xmlempty_addon_snippet.xmlleft_side.xmlright_side.xml
character
content.xmlcontent_button.xmlgui.xsdinventory
inventory_item_snippet.xmlmenu.xmlsrc
context
engine
entity_manager/src
map
rpg_components
Cargo.toml
resources
addon_snippet.xmlbook_snippet.xmlcircle.pnggui.xsditem_socket_snippet.xml
items
jewel_tooltip.xmlstat_type_snippet.xmlsrc
components
ability_slots.rsattributes.rscharacter_status.rscrafting_materials.rsinventory.rsitem_slots.rslevel.rsmacros.rsmod.rsnpc_type.rsstatistic_types.rsstatistics.rs
config
damage_type.rsitems
ability_addon.rsability_book.rsitem.rsitem_slots.rsitem_system.rsjewel.rsmap_item.rsmod.rsrarities.rstooltip.rs
lib.rs
|
@ -5,7 +5,6 @@ members = [
|
|||
"ConfigHandler",
|
||||
"Networking",
|
||||
"asset",
|
||||
"character_window",
|
||||
"context",
|
||||
"controllable_thread",
|
||||
"ecs",
|
||||
|
@ -15,12 +14,10 @@ members = [
|
|||
"gltf-loader",
|
||||
"loading-screen",
|
||||
"lua-wrapper",
|
||||
"map",
|
||||
"math",
|
||||
"presentation",
|
||||
"promise",
|
||||
"ring_buffer",
|
||||
"rpg_components",
|
||||
"scene_update_macros",
|
||||
"transaction_derive",
|
||||
]
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "character_window"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
destructure_traitobject = { workspace = true }
|
||||
downcast-rs = { workspace = true }
|
||||
|
||||
engine = { path = "../engine" }
|
||||
rpg_components = { path = "../rpg_components" }
|
|
@ -1,13 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="4" y_dim="1">
|
||||
<label id="equip" x_slot="0" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Equip</label>
|
||||
<label id="upgrade" x_slot="1" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Upgrade</label>
|
||||
<label id="salvage" x_slot="2" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Salvage</label>
|
||||
<label id="switch_mode" x_slot="3" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Switch</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="3" y_dim="1">
|
||||
<label id="socket" x_slot="0" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Socket</label>
|
||||
<label id="salvage" x_slot="1" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Salvage</label>
|
||||
<label id="switch_mode" x_slot="2" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Switch</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,9 +0,0 @@
|
|||
<root>
|
||||
<grid x_dim="1" y_dim="1" padding="2">
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1" background="#919191">
|
||||
<icon id="addon_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="addon_type" x_slot="1" y_slot="0" x_size="3" text_color="black" text_alignment="left">StatType</label>
|
||||
<label id="addon_value" x_slot="4" y_slot="0" x_size="2" text_color="black">StatValue</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,8 +0,0 @@
|
|||
<root>
|
||||
<grid x_dim="1" y_dim="1" padding="2">
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1" background="#919191">
|
||||
<icon x_slot="0" y_slot="0"></icon>
|
||||
<label id="info" x_slot="1" y_slot="0" x_size="3" text_color="black"></label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,16 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="10" margin="4" padding="4"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1"
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'>
|
||||
<button id="abilities" x_slot="1" y_slot="0" x_size="2" text_color="black">Abilities</button>
|
||||
<button id="addons" x_slot="3" y_slot="0" x_size="2" text_color="black">Addons</button>
|
||||
</grid>
|
||||
|
||||
<grid id="content" x_slot="0" y_slot="1" x_dim="1" y_dim="1" y_size="8"></grid>
|
||||
|
||||
<grid id="tooltip" x_slot="0" y_slot="9" x_dim="1" y_dim="1"></grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,35 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="10" padding="5" margin="5"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_normal='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_selected='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<grid x_slot="0" y_slot="0" x_dim="1" y_dim="9" y_size="9" padding="0" margin="0">
|
||||
<grid x_slot="0" y_slot="0" x_dim="4" y_dim="1" padding="0" margin="0">
|
||||
<button id="first_ability" x_slot="0" y_slot="0" fill_type="square" select_mode="none">0</button>
|
||||
<button id="second_ability" x_slot="1" y_slot="0" fill_type="square" select_mode="none">0</button>
|
||||
<button id="third_ability" x_slot="2" y_slot="0" fill_type="square" select_mode="none">0</button>
|
||||
<button id="fourth_ability" x_slot="3" y_slot="0" fill_type="square" select_mode="none">0</button>
|
||||
</grid>
|
||||
|
||||
<grid id="ability_content" x_slot="0" y_slot="1" x_dim="1" y_dim="8" y_size="8"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'></grid>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="9" x_dim="6" y_dim="1"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<icon id="common" x_slot="0" y_slot="0" fill_type="square"
|
||||
background='{"background_color":"white","border_color":"black","border_thickness":{"Pixel":2}}'>0</icon>
|
||||
<icon id="uncommon" x_slot="1" y_slot="0" fill_type="square"
|
||||
background='{"background_color":"white","border_color":"black","border_thickness":{"Pixel":2}}'>0</icon>
|
||||
<icon id="magical" x_slot="2" y_slot="0" fill_type="square"
|
||||
background='{"background_color":"white","border_color":"black","border_thickness":{"Pixel":2}}'>0</icon>
|
||||
<icon id="rare" x_slot="3" y_slot="0" fill_type="square"
|
||||
background='{"background_color":"white","border_color":"black","border_thickness":{"Pixel":2}}'>0</icon>
|
||||
<icon id="epic" x_slot="4" y_slot="0" fill_type="square"
|
||||
background='{"background_color":"white","border_color":"black","border_thickness":{"Pixel":2}}'>0</icon>
|
||||
<icon id="legendary" x_slot="5" y_slot="0" fill_type="square"
|
||||
background='{"background_color":"white","border_color":"black","border_thickness":{"Pixel":2}}'>0</icon>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,78 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid id="statistic_tab" x_dim="2" y_dim="1" padding="10">
|
||||
<!-- stats -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="2" y_dim="1" margin="0" padding="0"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<grid x_slot="0" y_slot="0" x_dim="1" y_dim="17" margin="2" padding="2">
|
||||
<label x_slot="0" y_slot="0" text_ratio="0.9">Air Resistance</label>
|
||||
<label x_slot="0" y_slot="1" text_ratio="0.9">Fire Resistance</label>
|
||||
<label x_slot="0" y_slot="2" text_ratio="0.9">Water Resistance</label>
|
||||
<label x_slot="0" y_slot="3" text_ratio="0.9">Armor</label>
|
||||
|
||||
<label x_slot="0" y_slot="5" text_ratio="0.9">Air Damage</label>
|
||||
<label x_slot="0" y_slot="6" text_ratio="0.9">Fire Damage</label>
|
||||
<label x_slot="0" y_slot="7" text_ratio="0.9">Water Damage</label>
|
||||
<label x_slot="0" y_slot="8" text_ratio="0.9">Physical Damage</label>
|
||||
|
||||
<label x_slot="0" y_slot="10" text_ratio="0.9">Crit Chance</label>
|
||||
<label x_slot="0" y_slot="11" text_ratio="0.9">Crit Damage</label>
|
||||
|
||||
<label x_slot="0" y_slot="13" text_ratio="0.9">Health</label>
|
||||
<label x_slot="0" y_slot="14" text_ratio="0.9">Mana</label>
|
||||
<label x_slot="0" y_slot="15" text_ratio="0.9">Health Regen</label>
|
||||
<label x_slot="0" y_slot="16" text_ratio="0.9">Mana Regen</label>
|
||||
</grid>
|
||||
<grid x_slot="1" y_slot="0" x_dim="1" y_dim="17" background="#aaaaaa" margin="2" padding="2">
|
||||
<label x_slot="0" y_slot="0" text_ratio="0.9" id="air_def_info">0</label>
|
||||
<label x_slot="0" y_slot="1" text_ratio="0.9" id="fire_def_info">0</label>
|
||||
<label x_slot="0" y_slot="2" text_ratio="0.9" id="water_def_info">0</label>
|
||||
<label x_slot="0" y_slot="3" text_ratio="0.9" id="armor_info">0</label>
|
||||
|
||||
<label x_slot="0" y_slot="5" text_ratio="0.9" id="air_dmg_info">0</label>
|
||||
<label x_slot="0" y_slot="6" text_ratio="0.9" id="fire_dmg_info">0</label>
|
||||
<label x_slot="0" y_slot="7" text_ratio="0.9" id="water_dmg_info"> 0</label>
|
||||
<label x_slot="0" y_slot="8" text_ratio="0.9" id="phys_dmg_info">0</label>
|
||||
|
||||
<label x_slot="0" y_slot="10" text_ratio="0.9" id="crit_chance_info">0</label>
|
||||
<label x_slot="0" y_slot="11" text_ratio="0.9" id="crit_dmg_info">0</label>
|
||||
|
||||
<label x_slot="0" y_slot="13" text_ratio="0.9" id="health_info">0</label>
|
||||
<label x_slot="0" y_slot="14" text_ratio="0.9" id="mana_info">0</label>
|
||||
<label x_slot="0" y_slot="15" text_ratio="0.9" id="health_regen_info">0</label>
|
||||
<label x_slot="0" y_slot="16" text_ratio="0.9" id="mana_regen_info">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="1" y_slot="0" x_dim="1" y_dim="17" margin="0" padding="0"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'>
|
||||
|
||||
<grid x_slot="0" y_slot="0" x_dim="2" y_dim="1">
|
||||
<label id="character_name" x_slot="0" y_slot="0" text_ratio="0.8">Name</label>
|
||||
<label id="level" x_slot="1" y_slot="0" text_ratio="0.8">0</label>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="1" x_dim="1" y_dim="1">
|
||||
<progressbar id="level_progress" x_slot="0" y_slot="0" background="#A69614" foreground="#E9D429">Test</progressbar>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="1" y_dim="4" y_size="4">
|
||||
<label id="attributes" x_slot="0" y_slot="0" text_color="black">Attributes</label>
|
||||
|
||||
<grid x_slot="0" y_slot="1" x_dim="4" y_dim="3" y_size="3">
|
||||
<label x_slot="0" y_slot="0" x_size="3" text_color="#C23519">Strength</label>
|
||||
<button id="strength_field" x_slot="3" y_slot="0" text_color="black" select="true">0</button>
|
||||
|
||||
<label x_slot="0" y_slot="1" x_size="3" text_color="#65D01E">Agility</label>
|
||||
<button id="agility_field" x_slot="3" y_slot="1" text_color="black">0</button>
|
||||
|
||||
<label x_slot="0" y_slot="2" x_size="3" text_color="#1D63B3">Intelligence</label>
|
||||
<button id="intelligence_field" x_slot="3" y_slot="2" text_color="black">0</button>
|
||||
</grid>
|
||||
</grid>
|
||||
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,14 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="7" margin="0" padding="0">
|
||||
<grid x_slot="0" y_slot="0" x_dim="3" y_dim="1"
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'>
|
||||
<button id="left" x_slot="0" y_slot="0" text_color="black" isolate="true">prev</button>
|
||||
<label id="tab_info" x_slot="1" y_slot="0" text_color="black">1 / 1</label>
|
||||
<button id="right" x_slot="2" y_slot="0" text_color="black" isolate="true">next</button>
|
||||
</grid>
|
||||
|
||||
<grid id="content" x_slot="0" y_slot="1" x_dim="4" y_dim="4" y_size="6"></grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="1" margin="0" padding="0"
|
||||
button_normal='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":0}}'
|
||||
button_selected='{"background_color":"#bd7000","border_color":"#c37417","border_thickness":{"Pixel":0}}'>
|
||||
<button id="button" x_slot="0" y_slot="0" select_mode="none" fill_type="square"></button>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,248 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<!-- definition of attributes -->
|
||||
<xs:attribute name="id" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="x_slot" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="y_slot" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="x_dim" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="y_dim" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="x_size" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="y_size" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="normal" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="selected" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="click_sound" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="hover_sound" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="select_mode">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="none"></xs:enumeration>
|
||||
<xs:enumeration value="bigger"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="icon" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="icon_margin" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="text_color" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="text_ratio" type="xs:decimal"></xs:attribute>
|
||||
<xs:attribute name="text_alignment">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="left"></xs:enumeration>
|
||||
<xs:enumeration value="right"></xs:enumeration>
|
||||
<xs:enumeration value="top"></xs:enumeration>
|
||||
<xs:enumeration value="bottom"></xs:enumeration>
|
||||
<xs:enumeration value="center"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="select" type="xs:boolean"></xs:attribute>
|
||||
<xs:attribute name="isolate" type="xs:boolean"></xs:attribute>
|
||||
<xs:attribute name="x_offset" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="y_offset" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="width" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="height" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="padding" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="margin" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="vert_align">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="top"></xs:enumeration>
|
||||
<xs:enumeration value="middle"></xs:enumeration>
|
||||
<xs:enumeration value="bottom"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="hori_align">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="left"></xs:enumeration>
|
||||
<xs:enumeration value="middle"></xs:enumeration>
|
||||
<xs:enumeration value="right"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="background" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="foreground" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="button_normal" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="button_selected" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="fill_type">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="expand"></xs:enumeration>
|
||||
<xs:enumeration value="square"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="reference_width" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="reference_height" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="layer" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="line_count" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="west_neighbour" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="east_neighbour" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="north_neighbour" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="south_neighbour" type="xs:string"></xs:attribute>
|
||||
|
||||
<!-- definition of complex elements -->
|
||||
<xs:element name="root">
|
||||
<xs:complexType>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element name="grid" />
|
||||
</xs:choice>
|
||||
<xs:attribute ref="reference_width"></xs:attribute>
|
||||
<xs:attribute ref="reference_height"></xs:attribute>
|
||||
<xs:attribute ref="layer"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="grid">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="grid" />
|
||||
<xs:element name="button" />
|
||||
<xs:element name="label" />
|
||||
<xs:element name="progressbar" />
|
||||
<xs:element name="textfield" />
|
||||
<xs:element name="icon" />
|
||||
</xs:choice>
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_dim"></xs:attribute>
|
||||
<xs:attribute ref="y_dim"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="x_offset"></xs:attribute>
|
||||
<xs:attribute ref="y_offset"></xs:attribute>
|
||||
<xs:attribute ref="width"></xs:attribute>
|
||||
<xs:attribute ref="height"></xs:attribute>
|
||||
<xs:attribute ref="padding"></xs:attribute>
|
||||
<xs:attribute ref="margin"></xs:attribute>
|
||||
<xs:attribute ref="vert_align"></xs:attribute>
|
||||
<xs:attribute ref="hori_align"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="button_normal"></xs:attribute>
|
||||
<xs:attribute ref="button_selected"></xs:attribute>
|
||||
<xs:attribute ref="click_sound"></xs:attribute>
|
||||
<xs:attribute ref="hover_sound"></xs:attribute>
|
||||
<xs:attribute ref="layer"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="button">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="select"></xs:attribute>
|
||||
<xs:attribute ref="isolate"></xs:attribute>
|
||||
<xs:attribute ref="normal"></xs:attribute>
|
||||
<xs:attribute ref="selected"></xs:attribute>
|
||||
<xs:attribute ref="fill_type"></xs:attribute>
|
||||
<xs:attribute ref="click_sound"></xs:attribute>
|
||||
<xs:attribute ref="hover_sound"></xs:attribute>
|
||||
<xs:attribute ref="select_mode"></xs:attribute>
|
||||
<xs:attribute ref="icon"></xs:attribute>
|
||||
<xs:attribute ref="icon_margin"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="west_neighbour"></xs:attribute>
|
||||
<xs:attribute ref="east_neighbour"></xs:attribute>
|
||||
<xs:attribute ref="north_neighbour"></xs:attribute>
|
||||
<xs:attribute ref="south_neighbour"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="label">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="multi_line_label">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="line_count"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="multi_line_textfield">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="line_count"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="progressbar">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="foreground"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="textfield">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="icon">
|
||||
<xs:complexType>
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="icon"></xs:attribute>
|
||||
<xs:attribute ref="margin"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="fill_type"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
|
@ -1,38 +0,0 @@
|
|||
<?xml-model href="../../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid id="equipment" x_dim="3" y_dim="5" padding="7" margin="5"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_normal='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_selected='{"background_color":"#6c440b","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<!-- amulets -->
|
||||
<button id="amulet_0" x_slot="0" y_slot="0" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
<button id="amulet_1" x_slot="2" y_slot="0" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- helmet -->
|
||||
<button id="helmet" x_slot="1" y_slot="0" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- chest plate -->
|
||||
<button id="chest" x_slot="1" y_slot="1" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- gloves -->
|
||||
<button id="gloves" x_slot="0" y_slot="1" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- belt -->
|
||||
<button id="belt" x_slot="1" y_slot="2" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- boots -->
|
||||
<button id="boots" x_slot="1" y_slot="4" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- main hand -->
|
||||
<button id="main hand" x_slot="0" y_slot="4" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- off hand -->
|
||||
<button id="off hand" x_slot="2" y_slot="4" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
|
||||
<!-- rings -->
|
||||
<button id="ring_0" x_slot="0" y_slot="2" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
<button id="ring_1" x_slot="2" y_slot="2" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
<button id="ring_2" x_slot="0" y_slot="3" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
<button id="ring_3" x_slot="2" y_slot="3" icon_margin="5" fill_type="square" select_mode="none"></button>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,13 +0,0 @@
|
|||
<?xml-model href="../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="4" y_dim="1">
|
||||
<label id="equip" x_slot="0" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Equip</label>
|
||||
<label id="salvage" x_slot="1" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Salvage</label>
|
||||
<label id="socket" x_slot="2" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Socket</label>
|
||||
<label id="switch_mode" x_slot="3" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Switch</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,23 +0,0 @@
|
|||
<?xml-model href="../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="10" padding="5" margin="5"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_normal='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
button_selected='{"background_color":"#c47503","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
|
||||
<grid x_slot="0" y_slot="0" x_dim="5" y_dim="5" y_size="9">
|
||||
<button id="reference" x_slot="2" y_slot="1" fill_type="square"></button>
|
||||
|
||||
<button id="first" x_slot="1" y_slot="3" fill_type="square"></button>
|
||||
<button id="second" x_slot="2" y_slot="3" fill_type="square"></button>
|
||||
<button id="third" x_slot="3" y_slot="3" fill_type="square"></button>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="9" x_dim="3" y_dim="1">
|
||||
<label id="combine" x_slot="1" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Combine</label>
|
||||
</grid>
|
||||
|
||||
|
||||
</grid>
|
||||
</root>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml-model href="../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="3" y_dim="1">
|
||||
<label id="socket" x_slot="0" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Socket</label>
|
||||
<label id="combine" x_slot="1" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Combine</label>
|
||||
<label id="switch_mode" x_slot="2" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Switch</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,17 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="10" margin="4" padding="4"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<grid x_slot="0" y_slot="0" x_dim="3" y_dim="1"
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'>
|
||||
<button id="items" x_slot="0" y_slot="0" text_color="black" isolate="true">Items</button>
|
||||
<button id="jewels" x_slot="1" y_slot="0" text_color="black" isolate="true">Jewels</button>
|
||||
<button id="maps" x_slot="2" y_slot="0" text_color="black" isolate="true">Maps</button>
|
||||
</grid>
|
||||
|
||||
<grid id="content" x_slot="0" y_slot="1" x_dim="1" y_dim="1" y_size="8"></grid>
|
||||
|
||||
<grid id="tooltip" x_slot="0" y_slot="9" x_dim="1" y_dim="1"></grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml-model href="../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="1" y_dim="1"
|
||||
background='{"background_color":"#945b09","border_color":"#543919","border_thickness":{"Pixel":2}}'>
|
||||
<label x_slot="0" y_slot="0" text_color="black">work in progress</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml-model href="../../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="3" y_dim="1">
|
||||
<label id="select" x_slot="0" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Select</label>
|
||||
<label id="start" x_slot="1" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Start</label>
|
||||
<label id="switch_mode" x_slot="2" y_slot="0" text_color="black"
|
||||
background='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'>Switch</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,15 +0,0 @@
|
|||
<root>
|
||||
<!-- loot slot -->
|
||||
<grid x_dim="1" y_dim="1" padding="5">
|
||||
<grid id="snippet_grid" x_slot="0" y_slot="0" x_dim="5" y_dim="1" background="#919191" button_normal="#919191" button_selected="#aaaaaa" margin="0">
|
||||
<button id="item_icon" x_slot="0" y_slot="0" select_mode="none"></button>
|
||||
|
||||
<!-- stats -->
|
||||
<label id="strength_info" x_slot="1" y_slot="0" text_ratio="0.7" text_color="#C23519">0</label>
|
||||
<label id="agility_info" x_slot="2" y_slot="0" text_ratio="0.7" text_color="#65D01E">0</label>
|
||||
<label id="intelligence_info" x_slot="3" y_slot="0" text_ratio="0.7" text_color="#1D63B3">0</label>
|
||||
|
||||
<button id="disassemble" x_slot="4" y_slot="0" select_mode="none"></button>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="10">
|
||||
<!-- header -->
|
||||
<grid x_dim="7" y_dim="1" x_offset="-420" y_offset="-310" width="840" height="50"
|
||||
vert_align="middle" hori_align="middle" margin="10" padding="10"
|
||||
button_normal='{"background_color":"#835219","border_color":"#543919","border_thickness":{"Pixel":5}}'
|
||||
button_selected='{"background_color":"#835219","border_color":"#c37417","border_thickness":{"Pixel":5}}'
|
||||
background='{"background_color":"#9c610a","border_color":"#543919","border_thickness":{"Pixel":5}}'>
|
||||
|
||||
<icon id="left_info" x_slot="1" y_slot="0"></icon>
|
||||
|
||||
<button id="open_statistics" x_slot="2" y_slot="0" text_ratio="0.7" text_color="black" select_mode="none" isolate="true">Character</button>
|
||||
<button id="open_inventory" x_slot="3" y_slot="0" text_ratio="0.7" text_color="black" select_mode="none" isolate="true">Inventory</button>
|
||||
<button id="open_abilities" x_slot="4" y_slot="0" text_ratio="0.7" text_color="black" select_mode="none" isolate="true">Abilities</button>
|
||||
|
||||
<icon id="right_info" x_slot="5" y_slot="0"></icon>
|
||||
|
||||
<grid x_slot="6" y_slot="0" x_dim="3" y_dim="1" padding="0" margin="0">
|
||||
<button id="close" x_slot="2" y_slot="0" text_ratio="0.6" text_color="black" isolate="true"
|
||||
normal='{"background_color":"#a00000","border_color":"#000000","border_thickness":{"Pixel":2}}'
|
||||
selected='{"background_color":"#df0707","border_color":"#000000","border_thickness":{"Pixel":2}}'
|
||||
select="true" select_mode="none">X</button>
|
||||
</grid>
|
||||
</grid>
|
||||
|
||||
<grid id="tab_content" x_dim="1" y_dim="1" x_offset="-400" y_offset="-260" width="800"
|
||||
height="500" vert_align="middle" hori_align="middle" margin="10" padding="10"
|
||||
background='{"background_color":"#B26F0C","border_color":"#543919","border_thickness":{"Pixel":5}}'> </grid>
|
||||
</root>
|
|
@ -1,273 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use rpg_components::components::ability_slots::AbilitySlots;
|
||||
use rpg_components::components::crafting_materials::CraftingMaterials;
|
||||
use rpg_components::components::inventory::Storable;
|
||||
use rpg_components::components::statistics::Statistics;
|
||||
use rpg_components::config::items::ItemSettings;
|
||||
use rpg_components::items::Rarities;
|
||||
|
||||
use crate::*;
|
||||
|
||||
use crate::{traits::RightSide, CharacterWindow};
|
||||
|
||||
pub struct AbilityPageRightSide<A: Ability + 'static> {
|
||||
snippet: Arc<GuiSnippet>,
|
||||
|
||||
ability_index: usize,
|
||||
|
||||
ability_marker: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> AbilityPageRightSide<A> {
|
||||
const ABILITY_BUTTON_NAMES: [&'static str; 4] = [
|
||||
"first_ability",
|
||||
"second_ability",
|
||||
"third_ability",
|
||||
"fourth_ability",
|
||||
];
|
||||
|
||||
pub fn new(
|
||||
engine: &Arc<Engine>,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
hero: Entity,
|
||||
) -> Result<Self> {
|
||||
let snippet = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/abilities/right_side.xml"),
|
||||
)?;
|
||||
|
||||
let color_settings = &engine
|
||||
.scene()
|
||||
.resources
|
||||
.get::<ItemSettings>()
|
||||
.rarity_color_settings;
|
||||
|
||||
Self::rarity_icon_background(&snippet, "common", color_settings.common)?;
|
||||
Self::rarity_icon_background(&snippet, "uncommon", color_settings.uncommon)?;
|
||||
Self::rarity_icon_background(&snippet, "magical", color_settings.magical)?;
|
||||
Self::rarity_icon_background(&snippet, "rare", color_settings.rare)?;
|
||||
Self::rarity_icon_background(&snippet, "epic", color_settings.epic)?;
|
||||
Self::rarity_icon_background(&snippet, "legendary", color_settings.legendary)?;
|
||||
|
||||
for (index, name) in Self::ABILITY_BUTTON_NAMES.iter().enumerate() {
|
||||
let button: Arc<Button> = snippet.element(name)?;
|
||||
|
||||
button.set_info_icon(&engine.controller_icon(ControllerButton::RightStick)?)?;
|
||||
|
||||
button.set_callback({
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities::<A>();
|
||||
|
||||
abilities.right_side.ability_index = index;
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
|
||||
move |selected| {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
if selected {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let abilities = entity.get_component::<AbilitySlots<A>>()?;
|
||||
|
||||
if let Some(book) = abilities.book(index) {
|
||||
let button = weak_button.upgrade().unwrap();
|
||||
let button_pos = button.position_extent();
|
||||
|
||||
let target_x = button_pos.0 + button_pos.2 as i32;
|
||||
let target_y = button_pos.1;
|
||||
|
||||
let statistics = entity.get_component::<Statistics>()?;
|
||||
|
||||
let gui = book.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
statistics,
|
||||
(target_x, target_y),
|
||||
)?;
|
||||
gui.enable()?;
|
||||
gui.perform_single_check(button_pos.0, button_pos.1)?;
|
||||
|
||||
menu.add_tooltip("active_ability", gui);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
menu.remove_tooltip("active_ability");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_custom_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |button| match button {
|
||||
ControllerButton::Y => {
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let abilities = multi_mut.get::<AbilitySlots<A>>()?;
|
||||
|
||||
if let Some(ability) = abilities.book_mut(index) {
|
||||
let materials = multi_mut.get::<CraftingMaterials>()?;
|
||||
|
||||
ability.upgrade(materials);
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
menu.tabs_mut()
|
||||
.abilities::<A>()
|
||||
.right_side
|
||||
.refresh(&engine, hero)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
_ => Ok(false),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
snippet,
|
||||
ability_index: 0,
|
||||
ability_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn selected_ability(&self) -> usize {
|
||||
self.ability_index
|
||||
}
|
||||
|
||||
fn rarity_icon_background(gui: &GuiSnippet, element: &str, color: Color) -> Result<()> {
|
||||
let icon: Arc<Icon> = gui.element(element)?;
|
||||
|
||||
icon.set_background(FillTypeInfo::Element(
|
||||
ElementDescriptor::new(color, Color::Black, 2),
|
||||
DisplayableFillType::Square,
|
||||
))
|
||||
}
|
||||
|
||||
fn update_crafting_count(&self, element: &str, value: u32) -> Result<()> {
|
||||
let icon: Arc<Icon> = self.snippet.element(element)?;
|
||||
icon.set_text(value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_ability_icon(&self, element: &str, image: Option<Arc<Image>>) -> Result<()> {
|
||||
let button: Arc<Button> = self.snippet.element(element)?;
|
||||
|
||||
match image {
|
||||
Some(image) => {
|
||||
button.set_icon(&image)?;
|
||||
}
|
||||
None => {
|
||||
button.clear_icon()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_active_ability(&self, engine: &Engine, abilities: &AbilitySlots<A>) -> Result<()> {
|
||||
let grid: Arc<Grid> = self.snippet.element("ability_content")?;
|
||||
let (_, rows) = grid.dimensions();
|
||||
|
||||
for y in 0..rows {
|
||||
grid.detach(0, y)?;
|
||||
}
|
||||
|
||||
if let Some(ability) = abilities.book(self.ability_index) {
|
||||
for (index, addon) in ability.addons().iter().enumerate() {
|
||||
match addon.as_ref() {
|
||||
Some(addon) => {
|
||||
let addon_type_snippet = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/abilities/addon_type_snippet.xml"),
|
||||
)?;
|
||||
|
||||
let addon_icon: Arc<Icon> = addon_type_snippet.element("addon_icon")?;
|
||||
let addon_type: Arc<Label> = addon_type_snippet.element("addon_type")?;
|
||||
let addon_value: Arc<Label> = addon_type_snippet.element("addon_value")?;
|
||||
|
||||
addon_icon.set_icon(&addon.icon())?;
|
||||
addon_type.set_text(&format!("{}", addon.addon_type()))?;
|
||||
addon_value.set_text(&addon.addon_type().val_as_str())?;
|
||||
|
||||
grid.attach(addon_type_snippet, 0, index, 1, 1)?;
|
||||
}
|
||||
None => {
|
||||
let empty_addon = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/abilities/empty_addon_snippet.xml"),
|
||||
)?;
|
||||
|
||||
grid.attach(empty_addon, 0, index, 1, 1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn next_ability(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
self.ability_index = (self.ability_index + 1) % 4;
|
||||
self.refresh(engine, hero)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> RightSide for AbilityPageRightSide<A> {
|
||||
fn refresh(&mut self, engine: &Engine, hero: Entity) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
|
||||
let crafting = entity.get_component::<CraftingMaterials>()?;
|
||||
|
||||
self.update_crafting_count("common", crafting.count(Rarities::Common))?;
|
||||
self.update_crafting_count("uncommon", crafting.count(Rarities::Uncommon))?;
|
||||
self.update_crafting_count("magical", crafting.count(Rarities::Magical))?;
|
||||
self.update_crafting_count("rare", crafting.count(Rarities::Rare))?;
|
||||
self.update_crafting_count("epic", crafting.count(Rarities::Epic))?;
|
||||
self.update_crafting_count("legendary", crafting.count(Rarities::Legendary))?;
|
||||
|
||||
let abilities = entity.get_component::<AbilitySlots<A>>()?;
|
||||
|
||||
for (index, name) in Self::ABILITY_BUTTON_NAMES.iter().enumerate() {
|
||||
self.update_ability_icon(name, abilities.book(index).map(|book| book.icon()))?;
|
||||
}
|
||||
|
||||
self.update_active_ability(engine, abilities)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.snippet
|
||||
}
|
||||
}
|
|
@ -1,306 +0,0 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use rpg_components::components::ability_slots::AbilitySlots;
|
||||
use rpg_components::components::inventory::{Inventory, Storable};
|
||||
use rpg_components::components::statistics::Statistics;
|
||||
use rpg_components::items::ability_addon::AbilityAddon;
|
||||
use rpg_components::items::ability_book::AbilityBook;
|
||||
|
||||
use crate::*;
|
||||
|
||||
use crate::{
|
||||
content::{Content, ContentUpdate},
|
||||
CharacterWindow,
|
||||
};
|
||||
|
||||
use super::AbilityPage;
|
||||
|
||||
impl<A: Ability + 'static> Content<A, AbilityAddon> {
|
||||
fn show_addon_tooltip(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
addon_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let addon = inventory.addon_at(addon_index);
|
||||
let gui = addon.create_tooltip(engine.gui_handler(), (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
gui.perform_single_check(x, y)?;
|
||||
|
||||
let window = reference.upgrade().unwrap();
|
||||
window.add_tooltip(format!("addon_{addon_index}"), gui);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_addon(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
addon_index: usize,
|
||||
ability_page: &AbilityPage<A>,
|
||||
) -> Result<()> {
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
let abilities = multi_mut.get::<AbilitySlots<A>>()?;
|
||||
|
||||
if let Some(book) = abilities.book_mut(ability_page.right_side.selected_ability()) {
|
||||
if book.has_free_addon_slots() {
|
||||
book.addons_mut()
|
||||
.insert_addon(inventory.remove_addon(addon_index));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> ContentUpdate for Content<A, AbilityAddon> {
|
||||
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(engine, |button, t, index| {
|
||||
button.set_icon(&t.icon())?;
|
||||
|
||||
button.set_custom_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
if let ControllerButton::X = controller_button {
|
||||
CharacterWindow::salvage_from_inventory::<A, _, _>(
|
||||
&engine,
|
||||
hero,
|
||||
|inventory| inventory.remove_addon(index),
|
||||
)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities::<A>();
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
Self::insert_addon(&engine, hero, index, abilities)?;
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_addon_tooltip(&engine, hero, index, &reference, button_pos)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("addon_{index}"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> Content<A, AbilityBook<A>> {
|
||||
fn equip_book(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
book_index: usize,
|
||||
ability_page: &AbilityPage<A>,
|
||||
) -> Result<()> {
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
let abilitiy_slots = multi_mut.get::<AbilitySlots<A>>()?;
|
||||
|
||||
if let Some(old_book) = abilitiy_slots.insert_book(
|
||||
inventory.remove_book(book_index),
|
||||
ability_page.right_side.selected_ability(),
|
||||
) {
|
||||
inventory.insert_book(old_book, book_index);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn show_book_tooltip(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
book_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
let statistics = entity.get_component::<Statistics>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let book = inventory.book_at(book_index);
|
||||
let gui =
|
||||
book.create_tooltip(engine.gui_handler(), statistics, (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
|
||||
let window = reference.upgrade().unwrap();
|
||||
let abilities = entity.get_component::<AbilitySlots<A>>()?;
|
||||
|
||||
match abilities.book(window.tabs().abilities::<A>().right_side.selected_ability()) {
|
||||
Some(selected_book) => {
|
||||
let button_pos = gui.position_extent();
|
||||
|
||||
let target_x = button_pos.0 + button_pos.2 as i32 + 2;
|
||||
let target_y = button_pos.1;
|
||||
|
||||
let compare_gui = selected_book.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
statistics,
|
||||
(target_x, target_y),
|
||||
)?;
|
||||
compare_gui.enable()?;
|
||||
gui.perform_double_check(&compare_gui, x, 2)?;
|
||||
|
||||
window.add_tooltip("active_book", compare_gui);
|
||||
}
|
||||
None => {
|
||||
gui.perform_single_check(x, y)?;
|
||||
}
|
||||
}
|
||||
|
||||
window.add_tooltip(format!("book_{book_index}"), gui);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> ContentUpdate for Content<A, AbilityBook<A>> {
|
||||
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(engine, |button, t, index| {
|
||||
button.set_icon(&t.icon())?;
|
||||
|
||||
button.set_custom_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
if let ControllerButton::X = controller_button {
|
||||
CharacterWindow::salvage_from_inventory::<A, _, _>(
|
||||
&engine,
|
||||
hero,
|
||||
|inventory| inventory.remove_book(index),
|
||||
)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities::<A>();
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let abilities = tabs.abilities();
|
||||
|
||||
Self::equip_book(&engine, hero, index, abilities)?;
|
||||
|
||||
abilities.update_page()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_book_tooltip(&engine, hero, index, &reference, button_pos)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("book_{index}"));
|
||||
window.remove_tooltip("active_book");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
|
@ -1,286 +0,0 @@
|
|||
mod ability_right_side;
|
||||
mod content;
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Result;
|
||||
use rpg_components::components::inventory::Inventory;
|
||||
|
||||
use self::ability_right_side::AbilityPageRightSide;
|
||||
|
||||
use super::{
|
||||
content::Content,
|
||||
page_content::{EmptyRightSide, PageContent},
|
||||
traits::{PageContentWrapper, RightSide},
|
||||
CharacterWindow, Page,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
pub struct AbilityPage<A: Ability + 'static> {
|
||||
close: Weak<Button>,
|
||||
|
||||
engine: Arc<Engine>,
|
||||
hero: Entity,
|
||||
|
||||
grid: Arc<Grid>,
|
||||
|
||||
tooltip: Arc<Grid>,
|
||||
content: Arc<Grid>,
|
||||
|
||||
modes: [Box<dyn PageContentWrapper>; 2],
|
||||
|
||||
current_mode: usize,
|
||||
|
||||
right_side: AbilityPageRightSide<A>,
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> AbilityPage<A> {
|
||||
pub fn new(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
reference: Weak<CharacterWindow>,
|
||||
close: &Arc<Button>,
|
||||
) -> Result<Self> {
|
||||
let grid = Grid::new(engine.gui_handler().clone(), 2, 1, false)?;
|
||||
|
||||
let left_base = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/abilities/left_side.xml"),
|
||||
)?;
|
||||
grid.attach(left_base.clone(), 0, 0, 1, 1)?;
|
||||
|
||||
Self::setup_content_switch(&left_base, reference.clone())?;
|
||||
|
||||
let tooltip = left_base.element("tooltip")?;
|
||||
let content = left_base.element("content")?;
|
||||
|
||||
// abilities
|
||||
let ability_mode = PageContent::new(
|
||||
Content::new::<_, Self>(&engine, reference.clone(), {
|
||||
let engine = engine.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let mut data = Vec::new();
|
||||
|
||||
engine.on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory<A>>()?;
|
||||
|
||||
data = inventory.iter_books().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/abilities/ability_tooltip.xml"),
|
||||
)?;
|
||||
|
||||
let equip: Arc<Label> = ui.element("equip")?;
|
||||
equip.set_info_icon(&engine.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let upgrade: Arc<Label> = ui.element("upgrade")?;
|
||||
upgrade.set_info_icon(&engine.controller_icon(ControllerButton::Y)?)?;
|
||||
|
||||
let salvage: Arc<Label> = ui.element("salvage")?;
|
||||
salvage.set_info_icon(&engine.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&engine.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
EmptyRightSide,
|
||||
);
|
||||
|
||||
// addons
|
||||
let addons_mode = PageContent::<A, _>::new(
|
||||
Content::new::<_, Self>(&engine, reference.clone(), {
|
||||
let engine = engine.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let mut data = Vec::new();
|
||||
|
||||
engine.on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory<A>>()?;
|
||||
|
||||
data = inventory.iter_addons().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/abilities/addon_tooltip.xml"),
|
||||
)?;
|
||||
|
||||
let equip: Arc<Label> = ui.element("socket")?;
|
||||
equip.set_info_icon(&engine.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let salvage: Arc<Label> = ui.element("salvage")?;
|
||||
salvage.set_info_icon(&engine.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&engine.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
EmptyRightSide,
|
||||
);
|
||||
|
||||
let right_side = AbilityPageRightSide::new(&engine, &reference, hero)?;
|
||||
|
||||
Ok(Self {
|
||||
close: Arc::downgrade(close),
|
||||
|
||||
engine: engine.clone(),
|
||||
hero,
|
||||
|
||||
grid,
|
||||
|
||||
tooltip,
|
||||
content,
|
||||
|
||||
modes: [Box::new(ability_mode), Box::new(addons_mode)],
|
||||
|
||||
current_mode: 0,
|
||||
|
||||
right_side,
|
||||
})
|
||||
}
|
||||
|
||||
fn update_page(&mut self) -> Result<()> {
|
||||
match self.current_mode {
|
||||
0 => println!("update ability view"),
|
||||
1 => println!("update addon view"),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
mode.content_mut().update(&self.engine, self.hero)?;
|
||||
|
||||
self.tooltip.attach(mode.tooltip().clone(), 0, 0, 1, 1)?;
|
||||
self.content
|
||||
.attach(mode.content_mut().base().clone(), 0, 0, 1, 1)?;
|
||||
|
||||
self.right_side.refresh(&self.engine, self.hero)?;
|
||||
self.grid
|
||||
.attach(self.right_side.base().clone(), 1, 0, 1, 1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_content_switch(
|
||||
left_base: &GuiSnippet,
|
||||
reference: Weak<CharacterWindow>,
|
||||
) -> Result<()> {
|
||||
let switch = {
|
||||
let reference = reference.clone();
|
||||
|
||||
move |index| {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let me = tabs.abilities::<A>();
|
||||
|
||||
if me.current_mode != index {
|
||||
me.current_mode = index;
|
||||
me.update_page()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
let switch_to_abilities = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(0)
|
||||
});
|
||||
|
||||
let switch_to_addons = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(1)
|
||||
});
|
||||
|
||||
left_base.set_click_callbacks(vec![
|
||||
("abilities", switch_to_abilities),
|
||||
("addons", switch_to_addons),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> Page for AbilityPage<A> {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>> {
|
||||
println!("enable AbilityPage");
|
||||
|
||||
for mode in self.modes.iter_mut() {
|
||||
mode.content_mut().refresh()?;
|
||||
}
|
||||
|
||||
self.update_page()?;
|
||||
|
||||
Ok(self.grid.clone())
|
||||
}
|
||||
|
||||
fn disable(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
let mode = &self.modes[self.current_mode];
|
||||
|
||||
mode.content().select()?;
|
||||
|
||||
if mode.content().is_empty() {
|
||||
if let Some(close) = self.close.upgrade() {
|
||||
close.select()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) -> Result<()> {
|
||||
self.modes[self.current_mode]
|
||||
.content_mut()
|
||||
.next_tab(&self.engine, self.hero)
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) -> Result<()> {
|
||||
self.modes[self.current_mode]
|
||||
.content_mut()
|
||||
.previous_tab(&self.engine, self.hero)
|
||||
}
|
||||
|
||||
fn event(&mut self, button: ControllerButton) -> Result<bool> {
|
||||
Ok(match button {
|
||||
ControllerButton::LeftStick => {
|
||||
self.current_mode = (self.current_mode + 1) % self.modes.len();
|
||||
self.update_page()?;
|
||||
self.select()?;
|
||||
true
|
||||
}
|
||||
|
||||
ControllerButton::RightStick => {
|
||||
self.right_side.next_ability(&self.engine, self.hero)?;
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Result;
|
||||
use rpg_components::{
|
||||
components::{
|
||||
attributes::{Agility, Attributes, Intelligence, Strength},
|
||||
item_slots::ItemSlotContainer,
|
||||
level::Level,
|
||||
statistics::Statistics,
|
||||
},
|
||||
config::{attributes::AttributeSettings, items::ItemSettings},
|
||||
};
|
||||
|
||||
use super::{CharacterWindow, Page};
|
||||
use crate::*;
|
||||
|
||||
pub struct CharacterPage {
|
||||
engine: Arc<Engine>,
|
||||
hero: Entity,
|
||||
|
||||
snippet: Arc<GuiSnippet>,
|
||||
grid: Arc<Grid>,
|
||||
}
|
||||
|
||||
impl CharacterPage {
|
||||
pub fn new(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
hero_name: &str,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> Result<Self> {
|
||||
let snippet = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/character/statistics.xml"),
|
||||
)?;
|
||||
|
||||
let grid: Arc<Grid> = snippet.element("statistic_tab")?;
|
||||
|
||||
let name: Arc<Label> = snippet.element("character_name")?;
|
||||
name.set_text(hero_name)?;
|
||||
|
||||
let strength: Arc<Button> = snippet.element("strength_field")?;
|
||||
strength.set_callback({
|
||||
let update_stats = Self::create_update_stats(hero, engine, reference);
|
||||
|
||||
move || {
|
||||
update_stats(|attributes| {
|
||||
attributes.add_strength(Strength::from(1));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let agility: Arc<Button> = snippet.element("agility_field")?;
|
||||
agility.set_callback({
|
||||
let update_stats = Self::create_update_stats(hero, engine, reference);
|
||||
|
||||
move || {
|
||||
update_stats(|attributes| {
|
||||
attributes.add_agility(Agility::from(1));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let intelligence: Arc<Button> = snippet.element("intelligence_field")?;
|
||||
intelligence.set_callback({
|
||||
let update_stats = Self::create_update_stats(hero, engine, reference);
|
||||
|
||||
move || {
|
||||
update_stats(|attributes| {
|
||||
attributes.add_intelligence(Intelligence::from(1));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
engine: engine.clone(),
|
||||
hero,
|
||||
|
||||
snippet,
|
||||
grid,
|
||||
})
|
||||
}
|
||||
|
||||
fn refresh(&self) -> Result<()> {
|
||||
self.update_stats()?;
|
||||
self.update_attributes()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_stats(&self) -> Result<()> {
|
||||
let air_def: Arc<Label> = self.snippet.element("air_def_info")?;
|
||||
let fire_def: Arc<Label> = self.snippet.element("fire_def_info")?;
|
||||
let water_def: Arc<Label> = self.snippet.element("water_def_info")?;
|
||||
let armor: Arc<Label> = self.snippet.element("armor_info")?;
|
||||
|
||||
let air_dmg: Arc<Label> = self.snippet.element("air_dmg_info")?;
|
||||
let fire_dmg: Arc<Label> = self.snippet.element("fire_dmg_info")?;
|
||||
let water_dmg: Arc<Label> = self.snippet.element("water_dmg_info")?;
|
||||
let phys_dmg: Arc<Label> = self.snippet.element("phys_dmg_info")?;
|
||||
|
||||
let crit_chance: Arc<Label> = self.snippet.element("crit_chance_info")?;
|
||||
let crit_dmg: Arc<Label> = self.snippet.element("crit_dmg_info")?;
|
||||
|
||||
let health: Arc<Label> = self.snippet.element("health_info")?;
|
||||
let health_regen: Arc<Label> = self.snippet.element("health_regen_info")?;
|
||||
let mana: Arc<Label> = self.snippet.element("mana_info")?;
|
||||
let mana_regen: Arc<Label> = self.snippet.element("mana_regen_info")?;
|
||||
|
||||
self.engine.on_scene(|scene| {
|
||||
let entity = scene.entity(self.hero)?;
|
||||
let statistics = entity.get_component::<Statistics>()?;
|
||||
|
||||
air_def.set_text(&format!("{}", statistics.air_resistance.raw()))?;
|
||||
fire_def.set_text(&format!("{}", statistics.fire_resistance.raw()))?;
|
||||
water_def.set_text(&format!("{}", statistics.water_resistance.raw()))?;
|
||||
armor.set_text(&format!("{}", statistics.armor.raw()))?;
|
||||
|
||||
air_dmg.set_text(&format!("{}", statistics.air_damage.raw()))?;
|
||||
fire_dmg.set_text(&format!("{}", statistics.fire_damage.raw()))?;
|
||||
water_dmg.set_text(&format!("{}", statistics.water_damage.raw()))?;
|
||||
phys_dmg.set_text(&format!("{}", statistics.physical_damage.raw()))?;
|
||||
|
||||
crit_chance.set_text(&format!("{:.2} %", statistics.critical_hit_chance.raw()))?;
|
||||
crit_dmg.set_text(&format!(
|
||||
"{:.2} %",
|
||||
statistics.critical_hit_damage.raw() + 100.0
|
||||
))?;
|
||||
|
||||
health.set_text(&format!("{:.0}", statistics.health.raw()))?;
|
||||
health_regen.set_text(&format!("{:.2}", statistics.health_regeneration.raw()))?;
|
||||
mana.set_text(&format!("{:.0}", statistics.mana.raw()))?;
|
||||
mana_regen.set_text(&format!("{:.2}", statistics.mana_regeneration.raw()))?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn update_attributes(&self) -> Result<()> {
|
||||
let level_label: Arc<Label> = self.snippet.element("level")?;
|
||||
let level_progress: Arc<ProgressBar> = self.snippet.element("level_progress")?;
|
||||
|
||||
let attributes_label: Arc<Label> = self.snippet.element("attributes")?;
|
||||
let strength: Arc<Button> = self.snippet.element("strength_field")?;
|
||||
let agility: Arc<Button> = self.snippet.element("agility_field")?;
|
||||
let intelligence: Arc<Button> = self.snippet.element("intelligence_field")?;
|
||||
|
||||
self.engine.on_scene(|scene| {
|
||||
let entity = scene.entity(self.hero)?;
|
||||
|
||||
let level = entity.get_component::<Level>()?;
|
||||
let attributes = entity.get_component::<Attributes>()?;
|
||||
|
||||
level_label.set_text(format!("Level: {}", level.level()))?;
|
||||
level_progress.set_text(format!(
|
||||
"{} / {}",
|
||||
level.current_experience, level.experience_needed
|
||||
))?;
|
||||
|
||||
attributes_label.set_text(format!(
|
||||
"Attributes ({})",
|
||||
Self::available_attribute_points(
|
||||
&scene.resources.get::<AttributeSettings>(),
|
||||
attributes,
|
||||
level
|
||||
)
|
||||
))?;
|
||||
|
||||
strength.set_text(attributes.strength().raw())?;
|
||||
agility.set_text(attributes.agility().raw())?;
|
||||
intelligence.set_text(attributes.intelligence().raw())?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn available_attribute_points(
|
||||
settings: &AttributeSettings,
|
||||
attributes: &Attributes,
|
||||
level: &Level,
|
||||
) -> u32 {
|
||||
let total_attribute_points = settings.meta_settings.starting_skill_points
|
||||
+ level.level() * settings.meta_settings.skill_points_per_level
|
||||
+ settings.starting_attributes.sum();
|
||||
|
||||
let attributes_spent = attributes.sum();
|
||||
|
||||
total_attribute_points - attributes_spent
|
||||
}
|
||||
|
||||
fn create_update_stats<F>(
|
||||
hero: Entity,
|
||||
engine: &Arc<Engine>,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> impl Fn(F) -> Result<()> + Clone
|
||||
where
|
||||
F: Fn(&mut Attributes),
|
||||
{
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |upgrade: F| {
|
||||
let mut upgraded = false;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let (resources, entity) = scene.entity_resource(hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let attribute_settings = resources.get::<AttributeSettings>();
|
||||
let item_settings = resources.get::<ItemSettings>();
|
||||
|
||||
let level = multi_mut.get::<Level>()?;
|
||||
let attributes = multi_mut.get::<Attributes>()?;
|
||||
|
||||
if Self::available_attribute_points(attribute_settings, attributes, level) > 0 {
|
||||
upgrade(attributes);
|
||||
|
||||
let statistics = multi_mut.get::<Statistics>()?;
|
||||
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
|
||||
statistics.update(attributes, attribute_settings, (&*items, item_settings));
|
||||
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if upgraded {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
menu.tabs().character().refresh()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Page for CharacterPage {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>> {
|
||||
println!("enable CharacterPage");
|
||||
|
||||
self.refresh()?;
|
||||
|
||||
Ok(self.grid.clone())
|
||||
}
|
||||
|
||||
fn disable(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
let strength: Arc<Button> = self.snippet.element("strength_field")?;
|
||||
strength.select()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn event(&mut self, _button: ControllerButton) -> Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
use std::{
|
||||
marker::PhantomData,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
|
||||
pub trait ContentWrapper: ContentUpdate + Send + Sync {
|
||||
fn refresh(&mut self) -> Result<()>;
|
||||
fn next_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()>;
|
||||
fn previous_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()>;
|
||||
fn base(&self) -> &Arc<GuiSnippet>;
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait ContentUpdate {
|
||||
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()>;
|
||||
fn select(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct Content<A: Ability + 'static, T: Send + Sync> {
|
||||
pub reference: Weak<CharacterWindow>,
|
||||
base: Arc<GuiSnippet>,
|
||||
data: Vec<T>,
|
||||
|
||||
on_enable: Box<dyn Fn() -> Result<Vec<T>> + Send + Sync + 'static>,
|
||||
|
||||
page: usize,
|
||||
pages: usize,
|
||||
|
||||
ability_marker: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static, T: Send + Sync> Content<A, T> {
|
||||
pub fn new<F, P>(
|
||||
engine: &Arc<Engine>,
|
||||
reference: Weak<CharacterWindow>,
|
||||
on_enable: F,
|
||||
) -> Result<Self>
|
||||
where
|
||||
F: Fn() -> Result<Vec<T>> + Send + Sync + 'static,
|
||||
P: Page,
|
||||
{
|
||||
let base = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../resources/content.xml"),
|
||||
)?;
|
||||
|
||||
let left: Arc<Button> = base.element("left")?;
|
||||
left.set_text("<")?;
|
||||
left.set_info_icon(&engine.controller_icon(ControllerButton::LeftTrigger)?)?;
|
||||
left.set_callback({
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(window) = reference.upgrade() {
|
||||
let mut tab = window.tab_mut();
|
||||
let page = tab.downcast_mut::<P>();
|
||||
|
||||
page.previous_tab()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
let right: Arc<Button> = base.element("right")?;
|
||||
right.set_text(">")?;
|
||||
right.set_info_icon(&engine.controller_icon(ControllerButton::RightTrigger)?)?;
|
||||
right.set_callback({
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
if let Some(window) = reference.upgrade() {
|
||||
let mut tab = window.tab_mut();
|
||||
let page = tab.downcast_mut::<P>();
|
||||
|
||||
page.next_tab()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
reference,
|
||||
base,
|
||||
data: Vec::new(),
|
||||
|
||||
on_enable: Box::new(on_enable),
|
||||
|
||||
page: 0,
|
||||
pages: 1,
|
||||
|
||||
ability_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn clear_grid(grid: &Arc<Grid>) -> Result<()> {
|
||||
let (rows, columns) = grid.dimensions();
|
||||
|
||||
for x in 0..columns {
|
||||
for y in 0..rows {
|
||||
grid.detach(x, y)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_tab(&self, label: &Arc<Label>) -> Result<()> {
|
||||
label.set_text(format!("{} / {}", self.page + 1, self.pages))
|
||||
}
|
||||
|
||||
pub fn update_base<F>(&mut self, engine: &Arc<Engine>, setup: F) -> Result<()>
|
||||
where
|
||||
Self: ContentWrapper,
|
||||
F: Fn(&Arc<Button>, &T, usize) -> Result<()>,
|
||||
{
|
||||
self.refresh()?;
|
||||
|
||||
let grid: Arc<Grid> = self.base.element("content")?;
|
||||
let label: Arc<Label> = self.base.element("tab_info")?;
|
||||
|
||||
Self::clear_grid(&grid)?;
|
||||
self.set_tab(&label)?;
|
||||
|
||||
let (rows, columns) = grid.dimensions();
|
||||
|
||||
'outer: for y in 0..rows {
|
||||
for x in 0..columns {
|
||||
let index = (self.page * columns * rows) + y * columns + x;
|
||||
|
||||
match self.data.get(index) {
|
||||
Some(t) => {
|
||||
let snippet = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../resources/content_button.xml"),
|
||||
)?;
|
||||
|
||||
let button: Arc<Button> = snippet.element("button")?;
|
||||
setup(&button, t, index)?;
|
||||
grid.attach(button, x, y, 1, 1)?;
|
||||
}
|
||||
None => break 'outer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select(&self) -> Result<()> {
|
||||
let grid: Arc<Grid> = self.base.element("content")?;
|
||||
|
||||
if let Some(child) = grid.child_at(0, 0)? {
|
||||
child.gridable().unwrap().selectable().unwrap().select()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static, T: Send + Sync> ContentWrapper for Content<A, T>
|
||||
where
|
||||
Content<A, T>: ContentUpdate,
|
||||
{
|
||||
fn refresh(&mut self) -> Result<()> {
|
||||
self.data = (self.on_enable)()?;
|
||||
|
||||
let grid: Arc<Grid> = self.base.element("content")?;
|
||||
let (rows, columns) = grid.dimensions();
|
||||
|
||||
self.pages = 1.max((self.data.len() as f32 / (rows * columns) as f32).ceil() as usize);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
if self.page < (self.pages - 1) {
|
||||
self.page += 1;
|
||||
self.update(engine, hero)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
if self.page > 0 {
|
||||
self.page -= 1;
|
||||
self.update(engine, hero)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use rpg_components::components::attributes::Attributes;
|
||||
use rpg_components::components::inventory::{Inventory, Storable};
|
||||
use rpg_components::components::item_slots::ItemSlotContainer;
|
||||
use rpg_components::components::statistics::Statistics;
|
||||
use rpg_components::config::attributes::AttributeSettings;
|
||||
use rpg_components::config::items::ItemSettings;
|
||||
use rpg_components::items::{Item, ItemAffix, Jewel, MapItem};
|
||||
|
||||
use crate::*;
|
||||
|
||||
use crate::{
|
||||
content::{Content, ContentUpdate},
|
||||
CharacterWindow,
|
||||
};
|
||||
|
||||
use super::jewel_right_side::{LowerJewels, ReferenceItemSource, ReferenceObject};
|
||||
|
||||
impl<A: Ability + 'static> Content<A, Item> {
|
||||
fn salvage_item(engine: &Arc<Engine>, hero: Entity, item_index: usize) -> Result<()> {
|
||||
CharacterWindow::salvage_from_inventory::<A, _, _>(engine, hero, |inventory| {
|
||||
let mut item = inventory.remove_item(item_index);
|
||||
|
||||
// unsocket jewels and add them into inventory
|
||||
item.affixes
|
||||
.iter_mut()
|
||||
.filter_map(|affix| match affix {
|
||||
ItemAffix::Socket(j) => j.take(),
|
||||
ItemAffix::Stat(_) => None,
|
||||
})
|
||||
.for_each(|jewel| {
|
||||
inventory.add_jewel(jewel);
|
||||
});
|
||||
|
||||
item
|
||||
})
|
||||
}
|
||||
|
||||
fn select_to_socket(engine: &Arc<Engine>, hero: Entity, item_index: usize) -> Result<bool> {
|
||||
let mut has_empty_sockets = true;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
let item = inventory.item_at(item_index).clone();
|
||||
|
||||
if item.affixes.iter().any(|affix| {
|
||||
if let ItemAffix::Socket(None) = affix {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
let socket_object = scene.resources.get_mut::<Option<ReferenceObject>>();
|
||||
|
||||
*socket_object = Some(ReferenceObject::Item {
|
||||
item,
|
||||
source: ReferenceItemSource::Inventory(item_index),
|
||||
});
|
||||
} else {
|
||||
has_empty_sockets = false;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(has_empty_sockets)
|
||||
}
|
||||
|
||||
fn equip_item(engine: &Arc<Engine>, hero: Entity, item_index: usize) -> Result<()> {
|
||||
engine.on_scene_mut(|scene| {
|
||||
let (resources, entity) = scene.entity_resource(hero)?;
|
||||
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let hero_items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
let attributes = multi_mut.get::<Attributes>()?;
|
||||
|
||||
// remove item from inventory
|
||||
let item = inventory.remove_item(item_index);
|
||||
|
||||
// add or swap items with equipment
|
||||
if let Some(old_item) = hero_items.insert(item.clone(), attributes, &mut multi_mut)? {
|
||||
inventory.insert_item(old_item, item_index);
|
||||
}
|
||||
|
||||
// update hero stats
|
||||
let statistics = multi_mut.get::<Statistics>()?;
|
||||
|
||||
statistics.update(
|
||||
attributes,
|
||||
resources.get::<AttributeSettings>(),
|
||||
(&*hero_items, resources.get::<ItemSettings>()),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn show_item_tooltip(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
item_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
let attributes = entity.get_component::<Attributes>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let item = inventory.item_at(item_index);
|
||||
let gui =
|
||||
item.create_tooltip(engine.gui_handler(), attributes, (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
|
||||
let window = reference.upgrade().unwrap();
|
||||
let items = entity.get_component::<ItemSlotContainer>()?;
|
||||
|
||||
match items.item_at(item.slot) {
|
||||
Some(equipped) => {
|
||||
let grid_pos = gui.position_extent();
|
||||
|
||||
let spacing = 2;
|
||||
let start_x = grid_pos.0 + grid_pos.2 as i32 + spacing;
|
||||
let start_y = grid_pos.1;
|
||||
|
||||
let compare_gui = equipped.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
attributes,
|
||||
(start_x, start_y),
|
||||
)?;
|
||||
compare_gui.enable()?;
|
||||
gui.perform_double_check(&compare_gui, x, spacing as u32)?;
|
||||
|
||||
window.add_tooltip("equip", compare_gui);
|
||||
}
|
||||
None => {
|
||||
gui.perform_single_check(x, y)?;
|
||||
}
|
||||
}
|
||||
|
||||
window.add_tooltip(format!("item_{item_index}"), gui);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> ContentUpdate for Content<A, Item> {
|
||||
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(engine, |button, t, index| {
|
||||
button.set_icon(&t.icon)?;
|
||||
|
||||
button.set_custom_callback({
|
||||
let reference = reference.clone();
|
||||
let engine = engine.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::X => {
|
||||
Self::salvage_item(&engine, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
ControllerButton::Y => {
|
||||
if Self::select_to_socket(&engine, hero, index)? {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.switch_to_jewels()?;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let reference = reference.clone();
|
||||
let engine = engine.clone();
|
||||
|
||||
move || {
|
||||
Self::equip_item(&engine, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let reference = reference.clone();
|
||||
let engine = engine.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_item_tooltip(&engine, hero, index, &reference, button_pos)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("item_{index}"));
|
||||
window.remove_tooltip("equip");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> Content<A, Jewel> {
|
||||
fn show_jewel_tooltip(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
item_index: usize,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let jewel = inventory.jewel_at(item_index);
|
||||
let gui = jewel.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
scene.resources.get::<ItemSettings>(),
|
||||
(target_x, target_y),
|
||||
)?;
|
||||
gui.enable()?;
|
||||
|
||||
gui.perform_single_check(x, y)?;
|
||||
reference
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.add_tooltip(format!("jewel_{item_index}"), gui);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn select_to_combine(engine: &Arc<Engine>, hero: Entity, jewel_index: usize) -> Result<()> {
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
let jewel = inventory.jewel_at(jewel_index).clone();
|
||||
|
||||
// add to reference
|
||||
let socket_object = scene.resources.get_mut::<Option<ReferenceObject>>();
|
||||
*socket_object = Some(ReferenceObject::Jewel {
|
||||
jewel,
|
||||
index: jewel_index,
|
||||
});
|
||||
|
||||
// remove from lower if placed there
|
||||
let lower_jewels = scene.resources.get_mut::<LowerJewels>();
|
||||
|
||||
if let Some(position) = lower_jewels.jewels.iter().position(|jewel| match jewel {
|
||||
Some((_, index)) => *index == jewel_index,
|
||||
None => false,
|
||||
}) {
|
||||
lower_jewels.jewels[position] = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select_to_lower(engine: &Arc<Engine>, hero: Entity, jewel_index: usize) -> Result<()> {
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity(hero)?;
|
||||
let inventory = entity.get_component::<Inventory<A>>()?;
|
||||
let jewel = inventory.jewel_at(jewel_index).clone();
|
||||
|
||||
// remove from reference if placed there
|
||||
let socket_object = scene.resources.get_mut::<Option<ReferenceObject>>();
|
||||
if let Some(ReferenceObject::Jewel { index, .. }) = socket_object {
|
||||
if *index == jewel_index {
|
||||
*socket_object = None;
|
||||
}
|
||||
}
|
||||
|
||||
let lower_jewels = scene.resources.get_mut::<LowerJewels>();
|
||||
|
||||
// check if that jewel is already added
|
||||
if !lower_jewels.jewels.iter().any(|content| match content {
|
||||
Some((_, index)) => *index == jewel_index,
|
||||
None => false,
|
||||
}) {
|
||||
// search for an empty position in lower jewels
|
||||
match lower_jewels.jewels.iter().position(|jewel| jewel.is_none()) {
|
||||
Some(position) => lower_jewels.jewels[position] = Some((jewel, jewel_index)),
|
||||
None => lower_jewels.jewels[0] = Some((jewel, jewel_index)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> ContentUpdate for Content<A, Jewel> {
|
||||
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
|
||||
let reference = self.reference.clone();
|
||||
|
||||
self.update_base(engine, |button, t, index| {
|
||||
button.set_icon(&t.icon())?;
|
||||
|
||||
button.set_select_callback({
|
||||
let weak_button = Arc::downgrade(&button);
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
let button_pos = weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
Self::show_jewel_tooltip(&engine, hero, index, &reference, button_pos)?;
|
||||
} else {
|
||||
let window = reference.upgrade().unwrap();
|
||||
|
||||
window.remove_tooltip(format!("jewel_{index}"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
Self::select_to_lower(&engine, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
button.set_custom_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::Y => {
|
||||
Self::select_to_combine(&engine, hero, index)?;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> ContentUpdate for Content<A, MapItem> {
|
||||
fn update(&mut self, engine: &Arc<Engine>, _hero: Entity) -> Result<()> {
|
||||
self.update_base(engine, |_button, _t, _index| {
|
||||
// button.set_icon(&t.icon)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
self.select()
|
||||
}
|
||||
}
|
|
@ -1,531 +0,0 @@
|
|||
use rpg_components::{
|
||||
components::{
|
||||
attributes::Attributes, character_status::CharacterStatus, inventory::Inventory,
|
||||
item_slots::ItemSlotContainer, statistics::Statistics,
|
||||
},
|
||||
config::{attributes::AttributeSettings, items::ItemSettings},
|
||||
items::{Item, ItemAffix, Tooltip},
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use super::{
|
||||
super::traits::*,
|
||||
jewel_right_side::{ReferenceItemSource, ReferenceObject},
|
||||
};
|
||||
|
||||
pub struct ItemRightSide {
|
||||
snippet: Arc<GuiSnippet>,
|
||||
|
||||
empty_icons: InventoryEmptyIcons,
|
||||
}
|
||||
|
||||
impl ItemRightSide {
|
||||
pub fn new<A: Ability + 'static>(
|
||||
engine: &Arc<Engine>,
|
||||
file: &str,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
hero: Entity,
|
||||
) -> Result<Self> {
|
||||
let snippet = GuiSnippet::from_str(engine.gui_handler(), file)?;
|
||||
let icons = InventoryEmptyIcons::new(engine)?;
|
||||
|
||||
let me = Self {
|
||||
snippet,
|
||||
empty_icons: icons,
|
||||
};
|
||||
|
||||
me.setup::<A>(engine, reference, hero)?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
fn setup<A: Ability + 'static>(
|
||||
&self,
|
||||
engine: &Arc<Engine>,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
hero: Entity,
|
||||
) -> Result<()> {
|
||||
button_setup!(self, engine, reference, hero, helmet, "helmet");
|
||||
button_setup!(self, engine, reference, hero, chest, "chest");
|
||||
button_setup!(self, engine, reference, hero, gloves, "gloves");
|
||||
button_setup!(self, engine, reference, hero, belt, "belt");
|
||||
button_setup!(self, engine, reference, hero, boots, "boots");
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, primary_hand, "main hand");
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, secondary_hand, "off hand");
|
||||
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, amulet, "amulet_0", 0);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, amulet, "amulet_1", 1);
|
||||
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, ring, "ring_0", 0);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, ring, "ring_1", 1);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, ring, "ring_2", 2);
|
||||
#[rustfmt::skip]
|
||||
button_setup!(self, engine, reference, hero, ring, "ring_3", 3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_icons(&self, items: &ItemSlotContainer) -> Result<()> {
|
||||
let ui = &self.snippet;
|
||||
let empty_icons = &self.empty_icons;
|
||||
|
||||
equip_update!(ui, items, helmet, empty_icons);
|
||||
equip_update!(ui, items, chest, empty_icons);
|
||||
equip_update!(ui, items, boots, empty_icons);
|
||||
equip_update!(ui, items, gloves, empty_icons);
|
||||
equip_update!(ui, items, belt, empty_icons);
|
||||
|
||||
equip_update!(ui, items, primary_hand, empty_icons, "main hand");
|
||||
equip_update!(ui, items, secondary_hand, empty_icons, "off hand");
|
||||
|
||||
equip_update!(ui, items, ring, empty_icons, "ring_0", 0);
|
||||
equip_update!(ui, items, ring, empty_icons, "ring_1", 1);
|
||||
equip_update!(ui, items, ring, empty_icons, "ring_2", 2);
|
||||
equip_update!(ui, items, ring, empty_icons, "ring_3", 3);
|
||||
|
||||
equip_update!(ui, items, amulet, empty_icons, "amulet_0", 0);
|
||||
equip_update!(ui, items, amulet, empty_icons, "amulet_1", 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_tooltip(
|
||||
engine: &Arc<Engine>,
|
||||
item: &Item,
|
||||
attributes: &Attributes,
|
||||
(x, y, w, _h): (i32, i32, u32, u32),
|
||||
) -> Result<Tooltip> {
|
||||
let target_x = x + w as i32;
|
||||
let target_y = y;
|
||||
|
||||
let gui = item.create_tooltip(engine.gui_handler(), attributes, (target_x, target_y))?;
|
||||
gui.enable()?;
|
||||
gui.perform_single_check(x, y)?;
|
||||
|
||||
Ok(gui)
|
||||
}
|
||||
}
|
||||
|
||||
impl RightSide for ItemRightSide {
|
||||
fn refresh(&mut self, engine: &Engine, hero: Entity) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
|
||||
let items = hero_object.get_component::<ItemSlotContainer>()?;
|
||||
|
||||
self.update_icons(items)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.snippet
|
||||
}
|
||||
}
|
||||
|
||||
struct InventoryEmptyIcons {
|
||||
helmet: Arc<Image>,
|
||||
chest: Arc<Image>,
|
||||
belt: Arc<Image>,
|
||||
boots: Arc<Image>,
|
||||
gloves: Arc<Image>,
|
||||
|
||||
primary_hand: Arc<Image>,
|
||||
secondary_hand: Arc<Image>,
|
||||
|
||||
ring: Arc<Image>,
|
||||
amulet: Arc<Image>,
|
||||
}
|
||||
|
||||
impl InventoryEmptyIcons {
|
||||
fn new(engine: &Engine) -> Result<Self> {
|
||||
let place_holder_settings = &engine
|
||||
.scene()
|
||||
.resources
|
||||
.get::<ItemSettings>()
|
||||
.icon_place_holder_paths;
|
||||
|
||||
Ok(Self {
|
||||
helmet: Image::from_file(place_holder_settings.helmet.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
chest: Image::from_file(place_holder_settings.chest.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
belt: Image::from_file(place_holder_settings.belt.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
boots: Image::from_file(place_holder_settings.boots.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
gloves: Image::from_file(place_holder_settings.gloves.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
|
||||
primary_hand: Image::from_file(place_holder_settings.main_hand.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
secondary_hand: Image::from_file(place_holder_settings.off_hand.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
|
||||
ring: Image::from_file(place_holder_settings.ring.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
amulet: Image::from_file(place_holder_settings.amulet.clone())?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mod macros {
|
||||
#[macro_export]
|
||||
macro_rules! button_setup {
|
||||
($self:ident, $engine:ident, $reference:ident, $hero:ident, $item:ident, $button:literal) => {
|
||||
paste::expr! {
|
||||
let [<$item _button>]: Arc<Button> = $self.snippet.element($button)?;
|
||||
|
||||
[<$item _button>].set_select_callback({
|
||||
let engine = $engine.clone();
|
||||
let reference = $reference.clone();
|
||||
let weak_button = Arc::downgrade(&[<$item _button>]);
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity($hero)?;
|
||||
|
||||
let attributes = entity.get_component::<Attributes>()?;
|
||||
let items = entity.get_component::<ItemSlotContainer>()?;
|
||||
|
||||
match items.$item() {
|
||||
Some($item) => {
|
||||
let button_pos =
|
||||
weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
let gui = Self::create_tooltip(
|
||||
&engine,
|
||||
$item,
|
||||
attributes,
|
||||
button_pos,
|
||||
)?;
|
||||
|
||||
reference.upgrade().unwrap().add_tooltip("equip", gui);
|
||||
}
|
||||
None => {
|
||||
reference.upgrade().unwrap().remove_tooltip("equip");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
reference.upgrade().unwrap().remove_tooltip("equip");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
[<$item _button>].set_callback({
|
||||
let engine = $engine.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move || {
|
||||
let mut found_item = false;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let (resources, entity) = scene.entity_resource($hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
|
||||
if let Some($item) = items.[<$item>]() {
|
||||
inventory.add_item($item.clone());
|
||||
found_item = true;
|
||||
}
|
||||
|
||||
if found_item {
|
||||
items.[<unset_ $item>](&mut multi_mut)?;
|
||||
|
||||
let statistics = multi_mut.get::<Statistics>()?;
|
||||
let attributes = multi_mut.get::<Attributes>()?;
|
||||
|
||||
statistics.update(
|
||||
attributes,
|
||||
resources.get::<AttributeSettings>(),
|
||||
(&*items, resources.get::<ItemSettings>())
|
||||
);
|
||||
|
||||
let status = multi_mut.get::<CharacterStatus>()?;
|
||||
|
||||
if status.current_health > statistics.health {
|
||||
status.current_health = statistics.health.clone();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if found_item {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
[<$item _button>].set_custom_callback({
|
||||
let engine = $engine.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::Y => {
|
||||
let mut empty_affixes_found = false;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut($hero)?;
|
||||
let items = entity.get_component::<ItemSlotContainer>()?;
|
||||
|
||||
if let Some(item) = items.$item().clone() {
|
||||
if item.affixes.iter().any(|affix| {
|
||||
if let ItemAffix::Socket(None) = affix {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
let socket_object = scene.resources.get_mut::<Option<ReferenceObject>>();
|
||||
|
||||
*socket_object = Some(ReferenceObject::Item {
|
||||
item,
|
||||
source: ReferenceItemSource::Slots(None),
|
||||
});
|
||||
|
||||
empty_affixes_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if empty_affixes_found {
|
||||
let window = reference.upgrade().unwrap();
|
||||
let mut tabs = window.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.switch_to_jewels()?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
($self:ident, $engine:ident, $reference:ident, $hero:ident, $item:ident, $button:literal, $index:literal) => {
|
||||
paste::expr! {
|
||||
let [<$item _button>]: Arc<Button> = $self.snippet.element($button)?;
|
||||
|
||||
[<$item _button>].set_select_callback({
|
||||
let engine = $engine.clone();
|
||||
let reference = $reference.clone();
|
||||
let weak_button = Arc::downgrade(&[<$item _button>]);
|
||||
|
||||
move |selected| {
|
||||
if selected {
|
||||
engine.on_scene(|scene| {
|
||||
let entity = scene.entity($hero)?;
|
||||
|
||||
let attributes = entity.get_component::<Attributes>()?;
|
||||
let items = entity.get_component::<ItemSlotContainer>()?;
|
||||
|
||||
match items.$item($index) {
|
||||
Some($item) => {
|
||||
let button_pos =
|
||||
weak_button.upgrade().unwrap().position_extent();
|
||||
|
||||
let gui = Self::create_tooltip(
|
||||
&engine,
|
||||
$item,
|
||||
attributes,
|
||||
button_pos,
|
||||
)?;
|
||||
|
||||
reference.upgrade().unwrap().add_tooltip("equip", gui);
|
||||
}
|
||||
None => {
|
||||
reference.upgrade().unwrap().remove_tooltip("equip");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
reference.upgrade().unwrap().remove_tooltip("equip");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
[<$item _button>].set_callback({
|
||||
let engine = $engine.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move || {
|
||||
let mut found_item = false;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let (resources, entity) = scene.entity_resource($hero)?;
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let items = multi_mut.get::<ItemSlotContainer>()?;
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
|
||||
if let Some($item) = items.[<$item>]($index) {
|
||||
inventory.add_item($item.clone());
|
||||
found_item = true;
|
||||
}
|
||||
|
||||
if found_item {
|
||||
items.[<unset_ $item>]($index)?;
|
||||
|
||||
let statistics = multi_mut.get::<Statistics>()?;
|
||||
let attributes = multi_mut.get::<Attributes>()?;
|
||||
|
||||
statistics.update(
|
||||
attributes,
|
||||
resources.get::<AttributeSettings>(),
|
||||
(&*items, resources.get::<ItemSettings>())
|
||||
);
|
||||
|
||||
let status = multi_mut.get::<CharacterStatus>()?;
|
||||
|
||||
if status.current_health > statistics.health {
|
||||
status.current_health = statistics.health.clone();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if found_item {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
[<$item _button>].set_custom_callback({
|
||||
let engine = $engine.clone();
|
||||
let reference = $reference.clone();
|
||||
|
||||
move |controller_button| {
|
||||
Ok(match controller_button {
|
||||
ControllerButton::Y => {
|
||||
let mut empty_affixes_found = false;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut($hero)?;
|
||||
let items = entity.get_component::<ItemSlotContainer>()?;
|
||||
|
||||
if let Some(item) = items.$item($index).clone() {
|
||||
if item.affixes.iter().any(|affix| {
|
||||
if let ItemAffix::Socket(None) = affix {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
let socket_object = scene.resources.get_mut::<Option<ReferenceObject>>();
|
||||
|
||||
*socket_object = Some(ReferenceObject::Item {
|
||||
item,
|
||||
source: ReferenceItemSource::Slots(Some($index)),
|
||||
});
|
||||
|
||||
empty_affixes_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if empty_affixes_found {
|
||||
let window = reference.upgrade().unwrap();
|
||||
let mut tabs = window.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.switch_to_jewels()?;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! equip_update {
|
||||
($gui:ident, $items:ident, $part:ident, $icons:ident) => {{
|
||||
let button: Arc<Button> = $gui.element(stringify!($part))?;
|
||||
|
||||
match $items.$part() {
|
||||
Some($part) => button.set_icon(&$part.icon)?,
|
||||
None => button.set_icon(&$icons.$part)?,
|
||||
}
|
||||
}};
|
||||
($gui:ident, $items:ident, $part:ident, $icons:ident, $name:literal) => {{
|
||||
let button: Arc<Button> = $gui.element($name)?;
|
||||
|
||||
match $items.$part() {
|
||||
Some($part) => button.set_icon(&$part.icon)?,
|
||||
None => button.set_icon(&$icons.$part)?,
|
||||
}
|
||||
}};
|
||||
($gui:ident, $items:ident, $part:ident, $icons:ident, $name:literal, $index:literal) => {{
|
||||
let button: Arc<Button> = $gui.element($name)?;
|
||||
|
||||
match $items.$part($index) {
|
||||
Some($part) => button.set_icon(&$part.icon)?,
|
||||
None => button.set_icon(&$icons.$part)?,
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
|
@ -1,415 +0,0 @@
|
|||
use rpg_components::{
|
||||
components::{
|
||||
attributes::Attributes,
|
||||
inventory::{Inventory, Storable},
|
||||
item_slots::ItemSlotContainer,
|
||||
statistics::Statistics,
|
||||
},
|
||||
config::{attributes::AttributeSettings, items::ItemSettings},
|
||||
items::{Item, ItemAffix, ItemSystem, Jewel},
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use crate::CharacterWindow;
|
||||
|
||||
use super::super::traits::*;
|
||||
|
||||
pub enum ReferenceItemSource {
|
||||
Inventory(usize),
|
||||
Slots(Option<usize>),
|
||||
}
|
||||
|
||||
pub enum ReferenceObject {
|
||||
Item {
|
||||
item: Item,
|
||||
source: ReferenceItemSource,
|
||||
},
|
||||
Jewel {
|
||||
jewel: Jewel,
|
||||
index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LowerJewels {
|
||||
pub jewels: [Option<(Jewel, usize)>; 3],
|
||||
}
|
||||
|
||||
pub struct JewelRightSide {
|
||||
snippet: Arc<GuiSnippet>,
|
||||
}
|
||||
|
||||
impl JewelRightSide {
|
||||
pub fn new<A: Ability + 'static>(
|
||||
engine: &Arc<Engine>,
|
||||
file: &str,
|
||||
hero: Entity,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> Result<Self> {
|
||||
let snippet = GuiSnippet::from_str(engine.gui_handler(), file)?;
|
||||
|
||||
let combine: Arc<Label> = snippet.element("combine")?;
|
||||
combine.set_info_icon(&engine.controller_icon(ControllerButton::RightStick)?)?;
|
||||
|
||||
engine.on_scene_mut(|scene| {
|
||||
scene
|
||||
.resources
|
||||
.insert_if_not_exists::<Option<ReferenceObject>>();
|
||||
scene.resources.insert_if_not_exists::<LowerJewels>();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let me = Self { snippet };
|
||||
me.setup_select::<A>(engine, hero, reference)?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
fn setup_select<A: Ability + 'static>(
|
||||
&self,
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
reference: &Weak<CharacterWindow>,
|
||||
) -> Result<()> {
|
||||
let (top, bottom) = self.elements()?;
|
||||
|
||||
top.set_select_callback({
|
||||
let engine = engine.clone();
|
||||
let weak_top = Arc::downgrade(&top);
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
let menu = reference.upgrade().unwrap();
|
||||
|
||||
if selected {
|
||||
let Some(button) = weak_top.upgrade() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let (x, y, w, _h) = button.position_extent();
|
||||
|
||||
let scene = engine.scene();
|
||||
|
||||
let reference_info = scene.resources.get::<Option<ReferenceObject>>();
|
||||
|
||||
if let Some(reference_info) = reference_info {
|
||||
let tooltip = match reference_info {
|
||||
ReferenceObject::Item { item, .. } => item.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
scene.entity(hero)?.get_component::<Attributes>()?,
|
||||
(x + w as i32, y),
|
||||
)?,
|
||||
ReferenceObject::Jewel { jewel, .. } => jewel.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
scene.resources.get::<ItemSettings>(),
|
||||
(x + w as i32, y),
|
||||
)?,
|
||||
};
|
||||
|
||||
tooltip.enable()?;
|
||||
tooltip.perform_single_check(x, y)?;
|
||||
|
||||
menu.add_tooltip("upper", tooltip);
|
||||
}
|
||||
} else {
|
||||
menu.remove_tooltip("upper");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
for (index, lower) in bottom.iter().enumerate() {
|
||||
lower.set_select_callback({
|
||||
let engine = engine.clone();
|
||||
let weak_top = Arc::downgrade(&lower);
|
||||
let reference = reference.clone();
|
||||
|
||||
move |selected| {
|
||||
let menu = reference.upgrade().unwrap();
|
||||
|
||||
if selected {
|
||||
let Some(button) = weak_top.upgrade() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let (x, y, w, _h) = button.position_extent();
|
||||
|
||||
let scene = engine.scene();
|
||||
|
||||
let lower_info = scene.resources.get::<LowerJewels>();
|
||||
|
||||
if let Some((lower_jewel, _)) = &lower_info.jewels[index] {
|
||||
let tooltip = lower_jewel.create_tooltip(
|
||||
engine.gui_handler(),
|
||||
scene.resources.get::<ItemSettings>(),
|
||||
(x + w as i32, y),
|
||||
)?;
|
||||
|
||||
tooltip.enable()?;
|
||||
tooltip.perform_single_check(x, y)?;
|
||||
|
||||
menu.add_tooltip(format!("lower_{index}",), tooltip);
|
||||
}
|
||||
} else {
|
||||
menu.remove_tooltip(format!("lower_{index}"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
lower.set_callback({
|
||||
let engine = engine.clone();
|
||||
let reference = reference.clone();
|
||||
|
||||
move || {
|
||||
let scene = engine.scene_mut();
|
||||
|
||||
let lower_info = scene.resources.get_mut::<LowerJewels>();
|
||||
|
||||
if lower_info.jewels[index].is_some() {
|
||||
lower_info.jewels[index] = None;
|
||||
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let inventory = tabs.inventory::<A>();
|
||||
|
||||
inventory.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn elements(&self) -> Result<(Arc<Button>, [Arc<Button>; 3])> {
|
||||
let reference_element: Arc<Button> = self.snippet.element("reference")?;
|
||||
|
||||
let first_element: Arc<Button> = self.snippet.element("first")?;
|
||||
let second_element: Arc<Button> = self.snippet.element("second")?;
|
||||
let third_element: Arc<Button> = self.snippet.element("third")?;
|
||||
|
||||
Ok((
|
||||
reference_element,
|
||||
[first_element, second_element, third_element],
|
||||
))
|
||||
}
|
||||
|
||||
pub fn clear(engine: &Arc<Engine>) {
|
||||
let scene = engine.scene_mut();
|
||||
|
||||
*scene.resources.get_mut::<Option<ReferenceObject>>() = None;
|
||||
scene
|
||||
.resources
|
||||
.get_mut::<LowerJewels>()
|
||||
.jewels
|
||||
.iter_mut()
|
||||
.for_each(|j| *j = None);
|
||||
}
|
||||
|
||||
pub fn combine<A: Ability + 'static>(engine: &Arc<Engine>, hero: Entity) -> Result<bool> {
|
||||
let scene = engine.scene_mut();
|
||||
|
||||
let (resources, entity) = scene.entity_resource(hero)?;
|
||||
let mut resources = resources.multi_mut();
|
||||
|
||||
let reference_info = resources.get::<Option<ReferenceObject>>();
|
||||
|
||||
if reference_info.is_none() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let lower_info = resources.get::<LowerJewels>();
|
||||
|
||||
if let Some(upper_info) = reference_info {
|
||||
match upper_info {
|
||||
ReferenceObject::Item { item, source } => {
|
||||
// check that is there something in lower
|
||||
// that can be socketed into the item
|
||||
if !lower_info.jewels.iter().any(|jewel| jewel.is_some()) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
match source {
|
||||
ReferenceItemSource::Inventory(index) => {
|
||||
let inventory = entity.get_component_mut::<Inventory<A>>()?;
|
||||
|
||||
let item = inventory.item_mut_at(*index);
|
||||
|
||||
match item.affixes.iter_mut().find(|affix| match affix {
|
||||
ItemAffix::Socket(opt) => opt.is_none(),
|
||||
ItemAffix::Stat(_) => false,
|
||||
}) {
|
||||
Some(ItemAffix::Socket(socket)) => {
|
||||
// we already made sure that lower is not empty -> unwrap is safe
|
||||
let (jewel, index) = lower_info.jewels[lower_info
|
||||
.jewels
|
||||
.iter()
|
||||
.position(|jewel| jewel.is_some())
|
||||
.unwrap()]
|
||||
.take()
|
||||
.unwrap();
|
||||
|
||||
*socket = Some(jewel);
|
||||
inventory.remove_jewel(index);
|
||||
}
|
||||
_ => {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReferenceItemSource::Slots(opt_index) => {
|
||||
let attribute_settings = resources.get::<AttributeSettings>();
|
||||
let item_settings = resources.get::<ItemSettings>();
|
||||
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
let item_slots = multi_mut.get::<ItemSlotContainer>()?;
|
||||
|
||||
let slot = item.slot;
|
||||
let item = item_slots.item_mut(slot, *opt_index).as_mut().unwrap();
|
||||
|
||||
match item.affixes.iter_mut().find(|affix| match affix {
|
||||
ItemAffix::Socket(opt) => opt.is_none(),
|
||||
ItemAffix::Stat(_) => false,
|
||||
}) {
|
||||
Some(ItemAffix::Socket(socket)) => {
|
||||
// we already made sure that lower is not empty -> unwrap is safe
|
||||
let (jewel, index) = lower_info.jewels[lower_info
|
||||
.jewels
|
||||
.iter()
|
||||
.position(|jewel| jewel.is_some())
|
||||
.unwrap()]
|
||||
.take()
|
||||
.unwrap();
|
||||
|
||||
*socket = Some(jewel);
|
||||
inventory.remove_jewel(index);
|
||||
}
|
||||
_ => {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
let statistics = multi_mut.get::<Statistics>()?;
|
||||
let attributes = multi_mut.get::<Attributes>()?;
|
||||
|
||||
statistics.update(
|
||||
attributes,
|
||||
attribute_settings,
|
||||
(&*item_slots, &*item_settings),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReferenceObject::Jewel { jewel, index } => {
|
||||
// check there are 3 jewels in lower
|
||||
// and that all jewels match rarity and level
|
||||
if !lower_info.jewels.iter().all(|j| match j {
|
||||
Some((lower_jewel, _)) => {
|
||||
jewel.rarity == lower_jewel.rarity && jewel.level == lower_jewel.level
|
||||
}
|
||||
None => false,
|
||||
}) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let item_settings = resources.get::<ItemSettings>();
|
||||
let item_system = resources.get::<ItemSystem<A>>();
|
||||
|
||||
let inventory = entity.get_component_mut::<Inventory<A>>()?;
|
||||
|
||||
let upper_jewel = inventory.jewel_mut_at(*index);
|
||||
|
||||
if upper_jewel.level >= 4 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
upper_jewel.level += 1;
|
||||
upper_jewel.update_stat(item_settings);
|
||||
upper_jewel.icon = Some(item_system.jewel_icon(
|
||||
upper_jewel.rarity,
|
||||
upper_jewel.level,
|
||||
upper_jewel.attribute,
|
||||
));
|
||||
|
||||
let mut jewels: Vec<(Jewel, usize)> = lower_info
|
||||
.jewels
|
||||
.iter_mut()
|
||||
.map(|j| j.take().unwrap())
|
||||
.collect();
|
||||
|
||||
jewels.sort_by_key(|(_, index)| Reverse(*index));
|
||||
jewels.iter().for_each(|(jewel, i)| {
|
||||
debug_assert_eq!(*jewel, inventory.remove_jewel(*i))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl RightSide for JewelRightSide {
|
||||
fn refresh(&mut self, engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
engine.on_scene(|scene| {
|
||||
let (reference, lower) = self.elements()?;
|
||||
|
||||
let reference_info = scene.resources.get::<Option<ReferenceObject>>();
|
||||
let lower_info = scene.resources.get::<LowerJewels>();
|
||||
|
||||
match reference_info.as_ref() {
|
||||
Some(reference_info) => {
|
||||
let icon = match reference_info {
|
||||
ReferenceObject::Item { item, .. } => item.icon(),
|
||||
ReferenceObject::Jewel { jewel, .. } => jewel.icon(),
|
||||
};
|
||||
|
||||
reference.set_icon(&icon)?;
|
||||
}
|
||||
None => reference.clear_icon()?,
|
||||
}
|
||||
|
||||
for (jewel, icon) in lower_info.jewels.iter().zip(lower.iter()) {
|
||||
match jewel.as_ref() {
|
||||
Some((jewel, _)) => icon.set_icon(&jewel.icon())?,
|
||||
None => icon.clear_icon()?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn disable(&mut self, engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
let scene = engine.scene_mut();
|
||||
|
||||
*scene.resources.get_mut::<Option<ReferenceObject>>() = None;
|
||||
scene
|
||||
.resources
|
||||
.get_mut::<LowerJewels>()
|
||||
.jewels
|
||||
.iter_mut()
|
||||
.for_each(|j| *j = None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.snippet
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::super::traits::*;
|
||||
|
||||
pub struct MapRightSide {
|
||||
snippet: Arc<GuiSnippet>,
|
||||
}
|
||||
|
||||
impl MapRightSide {
|
||||
pub fn new(engine: &Arc<Engine>, file: &str) -> Result<Self> {
|
||||
let snippet = GuiSnippet::from_str(engine.gui_handler(), file)?;
|
||||
|
||||
Ok(Self { snippet })
|
||||
}
|
||||
}
|
||||
|
||||
impl RightSide for MapRightSide {
|
||||
fn refresh(&mut self, _engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
&self.snippet
|
||||
}
|
||||
}
|
|
@ -1,446 +0,0 @@
|
|||
mod content;
|
||||
mod item_right_side;
|
||||
mod jewel_right_side;
|
||||
mod map_right_side;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use rpg_components::components::inventory::Inventory;
|
||||
use rpg_components::items::ability_book::Ability;
|
||||
|
||||
use super::page_content::PageContent;
|
||||
use super::traits::*;
|
||||
use super::{content::Content, CharacterWindow, Page};
|
||||
use item_right_side::ItemRightSide;
|
||||
use jewel_right_side::JewelRightSide;
|
||||
use map_right_side::MapRightSide;
|
||||
|
||||
pub struct InventoryPage<A: Ability + 'static> {
|
||||
close: Weak<Button>,
|
||||
|
||||
engine: Arc<Engine>,
|
||||
hero: Entity,
|
||||
|
||||
grid: Arc<Grid>,
|
||||
|
||||
tooltip: Arc<Grid>,
|
||||
content: Arc<Grid>,
|
||||
|
||||
modes: [Box<dyn PageContentWrapper>; 3],
|
||||
|
||||
current_mode: usize,
|
||||
|
||||
ability_marker: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> InventoryPage<A> {
|
||||
pub fn new(
|
||||
engine: &Arc<Engine>,
|
||||
hero: Entity,
|
||||
reference: Weak<CharacterWindow>,
|
||||
close: &Arc<Button>,
|
||||
) -> Result<Self> {
|
||||
let grid = Grid::new(engine.gui_handler().clone(), 2, 1, false)?;
|
||||
|
||||
let left_base = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/inventory/left_side.xml"),
|
||||
)?;
|
||||
|
||||
grid.attach(left_base.clone(), 0, 0, 1, 1)?;
|
||||
Self::setup_content_switch(&left_base, reference.clone())?;
|
||||
|
||||
let tooltip = left_base.element("tooltip")?;
|
||||
let content = left_base.element("content")?;
|
||||
|
||||
// items
|
||||
let item_mode = PageContent::<A, _>::new(
|
||||
Content::new::<_, Self>(engine, reference.clone(), {
|
||||
let engine = engine.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let mut data = Vec::new();
|
||||
|
||||
engine.on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory<A>>()?;
|
||||
|
||||
data = inventory.iter_items().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/inventory/items/tooltip.xml"),
|
||||
)?;
|
||||
|
||||
let equip: Arc<Label> = ui.element("equip")?;
|
||||
equip.set_info_icon(&engine.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let salvage: Arc<Label> = ui.element("salvage")?;
|
||||
salvage.set_info_icon(&engine.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let socket: Arc<Label> = ui.element("socket")?;
|
||||
socket.set_info_icon(&engine.controller_icon(ControllerButton::Y)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&engine.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
ItemRightSide::new::<A>(
|
||||
engine,
|
||||
include_str!("../../resources/inventory/items/right_side.xml"),
|
||||
&reference,
|
||||
hero,
|
||||
)?,
|
||||
);
|
||||
|
||||
// jewels
|
||||
let jewel_mode = PageContent::<A, _>::new(
|
||||
Content::new::<_, Self>(engine, reference.clone(), {
|
||||
let engine = engine.clone();
|
||||
let hero = hero.clone();
|
||||
|
||||
move || {
|
||||
let mut data = Vec::new();
|
||||
|
||||
engine.on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory<A>>()?;
|
||||
|
||||
data = inventory.iter_jewels().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/inventory/jewels/tooltip.xml"),
|
||||
)?;
|
||||
|
||||
let socket: Arc<Label> = ui.element("socket")?;
|
||||
socket.set_info_icon(&engine.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let combine: Arc<Label> = ui.element("combine")?;
|
||||
combine.set_info_icon(&engine.controller_icon(ControllerButton::Y)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&engine.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
JewelRightSide::new::<A>(
|
||||
engine,
|
||||
include_str!("../../resources/inventory/jewels/right_side.xml"),
|
||||
hero,
|
||||
&reference,
|
||||
)?,
|
||||
);
|
||||
|
||||
// maps
|
||||
let map_mode = PageContent::<A, _>::new(
|
||||
Content::new::<_, Self>(engine, reference.clone(), {
|
||||
let engine = engine.clone();
|
||||
let hero: Entity = hero.clone();
|
||||
|
||||
move || {
|
||||
let mut data = Vec::new();
|
||||
|
||||
engine.on_scene(|scene| {
|
||||
let hero_object = scene.entity(hero)?;
|
||||
let inventory = hero_object.get_component::<Inventory<A>>()?;
|
||||
|
||||
data = inventory.iter_maps().cloned().collect();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
})?,
|
||||
{
|
||||
let ui = GuiSnippet::from_str(
|
||||
engine.gui_handler(),
|
||||
include_str!("../../resources/inventory/maps/tooltip.xml"),
|
||||
)?;
|
||||
|
||||
let select: Arc<Label> = ui.element("select")?;
|
||||
select.set_info_icon(&engine.controller_icon(ControllerButton::A)?)?;
|
||||
|
||||
let start: Arc<Label> = ui.element("start")?;
|
||||
start.set_info_icon(&engine.controller_icon(ControllerButton::X)?)?;
|
||||
|
||||
let switch_mode: Arc<Label> = ui.element("switch_mode")?;
|
||||
switch_mode.set_info_icon(&engine.controller_icon(ControllerButton::LeftStick)?)?;
|
||||
|
||||
ui
|
||||
},
|
||||
MapRightSide::new(
|
||||
engine,
|
||||
include_str!("../../resources/inventory/maps/right_side.xml"),
|
||||
)?,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
close: Arc::downgrade(close),
|
||||
|
||||
engine: engine.clone(),
|
||||
hero,
|
||||
|
||||
grid,
|
||||
|
||||
tooltip,
|
||||
content,
|
||||
|
||||
modes: [
|
||||
Box::new(item_mode),
|
||||
Box::new(jewel_mode),
|
||||
Box::new(map_mode),
|
||||
],
|
||||
|
||||
current_mode: 0,
|
||||
|
||||
ability_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn switch_to_jewels(&mut self) -> Result<()> {
|
||||
self.current_mode = 1;
|
||||
self.update_page(true)
|
||||
}
|
||||
|
||||
fn update_page(&mut self, select: bool) -> Result<()> {
|
||||
match self.current_mode {
|
||||
0 => println!("update item view"),
|
||||
1 => println!("update jewel view"),
|
||||
2 => println!("update map view"),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
{
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
self.tooltip.attach(mode.tooltip().clone(), 0, 0, 1, 1)?;
|
||||
self.content
|
||||
.attach(mode.content_mut().base().clone(), 0, 0, 1, 1)?;
|
||||
self.grid
|
||||
.attach(mode.right_side_mut().base().clone(), 1, 0, 1, 1)?;
|
||||
|
||||
mode.content_mut().update(&self.engine, self.hero)?;
|
||||
mode.right_side_mut().refresh(&self.engine, self.hero)?;
|
||||
|
||||
if select {
|
||||
mode.content_mut().select()?;
|
||||
|
||||
if mode.content_mut().is_empty() {
|
||||
if let Some(close) = self.close.upgrade() {
|
||||
close.select()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.connect_character_page()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn connect_character_page(&mut self) -> Result<()> {
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
match self.current_mode {
|
||||
0 => {
|
||||
let right_side = mode.right_side_mut().base();
|
||||
let gloves: Arc<Button> = right_side.element("gloves")?;
|
||||
let ring_0: Arc<Button> = right_side.element("ring_0")?;
|
||||
let ring_2: Arc<Button> = right_side.element("ring_2")?;
|
||||
let main_hand: Arc<Button> = right_side.element("main hand")?;
|
||||
|
||||
let content_grid: Arc<Grid> = mode.content_mut().base().element("content")?;
|
||||
|
||||
content_grid.connect(
|
||||
ConnectDirection::East,
|
||||
[
|
||||
(0, gloves.selectable().unwrap()),
|
||||
(1, ring_0.selectable().unwrap()),
|
||||
(2, ring_2.selectable().unwrap()),
|
||||
(3, main_hand.selectable().unwrap()),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
1 => {
|
||||
let right_side = mode.right_side_mut().base();
|
||||
|
||||
let reference: Arc<Button> = right_side.element("reference")?;
|
||||
let first: Arc<Button> = right_side.element("first")?;
|
||||
|
||||
let content_grid: Arc<Grid> = mode.content_mut().base().element("content")?;
|
||||
|
||||
content_grid.connect(
|
||||
ConnectDirection::East,
|
||||
[
|
||||
(1, reference.selectable().unwrap()),
|
||||
(0, reference.selectable().unwrap()),
|
||||
(3, first.selectable().unwrap()),
|
||||
(2, first.selectable().unwrap()),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
2 => {
|
||||
// map tab is not implemented now
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_content_switch(
|
||||
left_base: &GuiSnippet,
|
||||
reference: Weak<CharacterWindow>,
|
||||
) -> Result<()> {
|
||||
let switch = {
|
||||
let reference = reference.clone();
|
||||
|
||||
move |index| {
|
||||
if let Some(menu) = reference.upgrade() {
|
||||
let mut tabs = menu.tabs_mut();
|
||||
let me = tabs.inventory::<A>();
|
||||
|
||||
if me.current_mode != index {
|
||||
me.current_mode = index;
|
||||
me.update_page(true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
let switch_to_items = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(0)
|
||||
});
|
||||
|
||||
let switch_to_jewels = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(1)
|
||||
});
|
||||
|
||||
let switch_to_maps = Box::new({
|
||||
let switch = switch.clone();
|
||||
|
||||
move || switch(2)
|
||||
});
|
||||
|
||||
left_base.set_click_callbacks(vec![
|
||||
("items", switch_to_items),
|
||||
("jewels", switch_to_jewels),
|
||||
("maps", switch_to_maps),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> Page for InventoryPage<A> {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>> {
|
||||
println!("enable InventoryPage");
|
||||
|
||||
for mode in self.modes.iter_mut() {
|
||||
mode.content_mut().refresh()?;
|
||||
}
|
||||
|
||||
self.update_page(false)?;
|
||||
|
||||
Ok(self.grid.clone())
|
||||
}
|
||||
|
||||
fn disable(&mut self) -> Result<()> {
|
||||
self.modes[self.current_mode]
|
||||
.right_side_mut()
|
||||
.disable(&self.engine, self.hero)
|
||||
}
|
||||
|
||||
fn select(&self) -> Result<()> {
|
||||
let mode = &self.modes[self.current_mode];
|
||||
|
||||
mode.content().select()?;
|
||||
|
||||
if mode.content().is_empty() {
|
||||
if let Some(close) = self.close.upgrade() {
|
||||
close.select()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) -> Result<()> {
|
||||
{
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
mode.content_mut().next_tab(&self.engine, self.hero)?;
|
||||
mode.content_mut().select()?;
|
||||
}
|
||||
|
||||
self.connect_character_page()
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) -> Result<()> {
|
||||
{
|
||||
let mode = &mut self.modes[self.current_mode];
|
||||
|
||||
mode.content_mut().previous_tab(&self.engine, self.hero)?;
|
||||
mode.content_mut().select()?;
|
||||
}
|
||||
|
||||
self.connect_character_page()
|
||||
}
|
||||
|
||||
fn event(&mut self, button: ControllerButton) -> Result<bool> {
|
||||
Ok(match button {
|
||||
ControllerButton::LeftStick => {
|
||||
self.modes[self.current_mode]
|
||||
.right_side_mut()
|
||||
.disable(&self.engine, self.hero)?;
|
||||
|
||||
self.current_mode = (self.current_mode + 1) % self.modes.len();
|
||||
|
||||
self.update_page(true)?;
|
||||
true
|
||||
}
|
||||
ControllerButton::RightStick => {
|
||||
// check if jewel page is open
|
||||
if self.current_mode == 1 {
|
||||
if JewelRightSide::combine::<A>(&self.engine, self.hero)? {
|
||||
JewelRightSide::clear(&self.engine);
|
||||
self.update_page(true)?;
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,402 +0,0 @@
|
|||
mod abilities;
|
||||
mod character;
|
||||
mod content;
|
||||
mod inventory;
|
||||
mod page_content;
|
||||
mod traits;
|
||||
|
||||
use anyhow::Result;
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use engine::prelude::*;
|
||||
use rpg_components::{
|
||||
components::{
|
||||
crafting_materials::CraftingMaterials,
|
||||
inventory::{Inventory, Storable},
|
||||
},
|
||||
items::ability_book::Ability,
|
||||
};
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||
},
|
||||
};
|
||||
|
||||
use self::{abilities::AbilityPage, character::CharacterPage, inventory::InventoryPage};
|
||||
|
||||
trait Page: Any + Send + Sync + Downcast {
|
||||
fn enable(&mut self) -> Result<Arc<Grid>>;
|
||||
fn disable(&mut self) -> Result<()>;
|
||||
fn select(&self) -> Result<()>;
|
||||
fn next_tab(&mut self) -> Result<()>;
|
||||
fn previous_tab(&mut self) -> Result<()>;
|
||||
fn event(&mut self, button: ControllerButton) -> Result<bool>;
|
||||
}
|
||||
|
||||
impl_downcast!(Page);
|
||||
|
||||
struct Tab<'a> {
|
||||
index: usize,
|
||||
tabs: RwLockReadGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for Tab<'a> {
|
||||
type Target = Box<dyn Page>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tabs[self.index]
|
||||
}
|
||||
}
|
||||
|
||||
struct TabMut<'a> {
|
||||
index: usize,
|
||||
tabs: RwLockWriteGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
impl<'a> TabMut<'a> {
|
||||
pub fn downcast_mut<T: Page>(&mut self) -> &mut T {
|
||||
self.tabs[self.index].downcast_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for TabMut<'a> {
|
||||
type Target = Box<dyn Page>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tabs[self.index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for TabMut<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.tabs[self.index]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tabs<'a> {
|
||||
tabs: RwLockReadGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<'a> Tabs<'a> {
|
||||
pub fn character(&mut self) -> &CharacterPage {
|
||||
self.tabs[0].downcast_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn inventory<A: Ability + 'static>(&mut self) -> &InventoryPage<A> {
|
||||
self.tabs[1].downcast_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn abilities<A: Ability + 'static>(&mut self) -> &AbilityPage<A> {
|
||||
self.tabs[2].downcast_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TabsMut<'a> {
|
||||
tabs: RwLockWriteGuard<'a, [Box<dyn Page>; 3]>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<'a> TabsMut<'a> {
|
||||
pub fn character(&mut self) -> &mut CharacterPage {
|
||||
self.tabs[0].downcast_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn inventory<A: Ability + 'static>(&mut self) -> &mut InventoryPage<A> {
|
||||
self.tabs[1].downcast_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn abilities<A: Ability + 'static>(&mut self) -> &mut AbilityPage<A> {
|
||||
self.tabs[2].downcast_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CharacterWindow {
|
||||
close: Box<dyn FutureStateChange>,
|
||||
|
||||
menu_gui: Arc<GuiBuilder>,
|
||||
tab_content_grid: Arc<Grid>,
|
||||
|
||||
tooltips: Mutex<HashMap<String, Arc<GuiBuilder>>>,
|
||||
|
||||
tabs: RwLock<[Box<dyn Page>; 3]>,
|
||||
tab: AtomicUsize,
|
||||
|
||||
engine: Arc<Engine>,
|
||||
}
|
||||
|
||||
impl CharacterWindow {
|
||||
pub fn new<A: Ability + 'static>(
|
||||
engine: Arc<Engine>,
|
||||
hero: Entity,
|
||||
name: &str,
|
||||
close: Box<dyn FutureStateChange>,
|
||||
) -> Result<Arc<Self>> {
|
||||
let menu_gui =
|
||||
GuiBuilder::from_str(engine.gui_handler(), include_str!("../resources/menu.xml"))?;
|
||||
|
||||
let content_grid = menu_gui.element("tab_content")?;
|
||||
let open_character_page: Arc<Button> = menu_gui.element("open_statistics")?;
|
||||
let open_inventory_page: Arc<Button> = menu_gui.element("open_inventory")?;
|
||||
let open_ability_page: Arc<Button> = menu_gui.element("open_abilities")?;
|
||||
let close_button: Arc<Button> = menu_gui.element("close")?;
|
||||
|
||||
let character_window = Arc::new_cyclic(|me| CharacterWindow {
|
||||
close,
|
||||
|
||||
menu_gui,
|
||||
tab_content_grid: content_grid,
|
||||
|
||||
tooltips: Mutex::default(),
|
||||
|
||||
tabs: RwLock::new([
|
||||
Box::new(CharacterPage::new(&engine, hero, name, me).unwrap()),
|
||||
Box::new(
|
||||
InventoryPage::<A>::new(&engine, hero, me.clone(), &close_button).unwrap(),
|
||||
),
|
||||
Box::new(AbilityPage::<A>::new(&engine, hero, me.clone(), &close_button).unwrap()),
|
||||
]),
|
||||
tab: AtomicUsize::new(0),
|
||||
|
||||
engine,
|
||||
});
|
||||
|
||||
let open_tab = {
|
||||
let weak_me = Arc::downgrade(&character_window);
|
||||
|
||||
move |index| {
|
||||
if let Some(me) = weak_me.upgrade() {
|
||||
me.tab.store(index, SeqCst);
|
||||
|
||||
me.tab_content_grid
|
||||
.attach(me.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
me.tab().select()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
open_character_page.set_callback({
|
||||
let open_tab = open_tab.clone();
|
||||
|
||||
move || open_tab(0)
|
||||
});
|
||||
|
||||
open_inventory_page.set_callback({
|
||||
let open_tab = open_tab.clone();
|
||||
|
||||
move || open_tab(1)
|
||||
});
|
||||
|
||||
open_ability_page.set_callback({
|
||||
let open_tab = open_tab.clone();
|
||||
|
||||
move || open_tab(2)
|
||||
});
|
||||
|
||||
Self::setup_menu(&character_window)?;
|
||||
|
||||
Ok(character_window)
|
||||
}
|
||||
|
||||
pub fn event(&self, button: ControllerButton) -> Result<bool> {
|
||||
self.tabs.write().unwrap()[self.tab.load(SeqCst)].event(button)
|
||||
}
|
||||
|
||||
pub fn tabs<'a>(&'a self) -> Tabs<'a> {
|
||||
Tabs {
|
||||
tabs: self.tabs.read().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tabs_mut<'a>(&'a self) -> TabsMut<'a> {
|
||||
TabsMut {
|
||||
tabs: self.tabs.write().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tab(&self) -> Tab<'_> {
|
||||
Tab {
|
||||
index: self.tab.load(SeqCst),
|
||||
tabs: self.tabs.read().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_mut(&self) -> TabMut<'_> {
|
||||
TabMut {
|
||||
index: self.tab.load(SeqCst),
|
||||
tabs: self.tabs.write().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tooltip(&self, name: impl ToString, gui: impl Into<Arc<GuiBuilder>>) {
|
||||
self.tooltips
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(name.to_string(), gui.into());
|
||||
}
|
||||
|
||||
pub fn remove_tooltip(&self, name: impl ToString) {
|
||||
self.tooltips.lock().unwrap().remove(&name.to_string());
|
||||
}
|
||||
|
||||
pub fn salvage_from_inventory<A, F, S>(engine: &Engine, hero: Entity, f: F) -> Result<()>
|
||||
where
|
||||
A: Ability + 'static,
|
||||
F: FnOnce(&mut Inventory<A>) -> S,
|
||||
S: Storable,
|
||||
{
|
||||
engine.on_scene_mut(|scene| {
|
||||
let entity = scene.entity_mut(hero)?;
|
||||
|
||||
let mut multi_mut = entity.multi_mut();
|
||||
|
||||
let crafting_materials = multi_mut.get::<CraftingMaterials>()?;
|
||||
let inventory = multi_mut.get::<Inventory<A>>()?;
|
||||
|
||||
// remove callback
|
||||
let storable = f(inventory);
|
||||
|
||||
crafting_materials.increment(storable.rarity());
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TopLevelGui for CharacterWindow {
|
||||
fn gui_traits(&self) -> &dyn GuiElementTraits {
|
||||
self
|
||||
}
|
||||
|
||||
fn top_gui(&self) -> Option<&dyn TopGui> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn elements(&self) -> Option<&HashMap<String, UiElement>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn functionality(&self) -> Option<&dyn Functionality> {
|
||||
None
|
||||
}
|
||||
|
||||
fn enable(&self) -> Result<()> {
|
||||
self.menu_gui.enable()?;
|
||||
|
||||
self.tab_content_grid
|
||||
.attach(self.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
self.tab().select()?;
|
||||
|
||||
let close_button: Arc<Button> = self.menu_gui.element("close")?;
|
||||
close_button.set_info_icon(&self.engine.controller_icon(ControllerButton::B)?)?;
|
||||
|
||||
let left_info: Arc<Icon> = self.menu_gui.element("left_info")?;
|
||||
left_info.set_icon(&self.engine.controller_icon(ControllerButton::LeftButton)?)?;
|
||||
|
||||
let right_info: Arc<Icon> = self.menu_gui.element("right_info")?;
|
||||
right_info.set_icon(&self.engine.controller_icon(ControllerButton::RightButton)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&self) -> Result<()> {
|
||||
self.menu_gui.disable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiElementTraits for CharacterWindow {
|
||||
fn gridable(&self) -> Option<&dyn Gridable> {
|
||||
None
|
||||
}
|
||||
|
||||
fn visibility(&self) -> Option<&dyn Visibility> {
|
||||
None
|
||||
}
|
||||
|
||||
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl TopGui for CharacterWindow {
|
||||
fn decline(&self) -> Result<()> {
|
||||
(self.close)()
|
||||
}
|
||||
|
||||
fn next_tab(&self, second_level: bool) -> Result<()> {
|
||||
match second_level {
|
||||
false => {
|
||||
// disable old tab
|
||||
self.tab_mut().disable()?;
|
||||
|
||||
// add to tab index
|
||||
self.tab.store((self.tab.load(SeqCst) + 1) % 3, SeqCst);
|
||||
|
||||
// update tab content
|
||||
self.tab_content_grid
|
||||
.attach(self.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
self.tab().select()?;
|
||||
}
|
||||
true => {
|
||||
self.tab_mut().next_tab()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous_tab(&self, second_level: bool) -> Result<()> {
|
||||
match second_level {
|
||||
false => {
|
||||
// disable old tab
|
||||
self.tab_mut().disable()?;
|
||||
|
||||
// subtract from tab index
|
||||
if self.tab.load(SeqCst) == 0 {
|
||||
self.tab.store(2, SeqCst);
|
||||
} else {
|
||||
self.tab.store(self.tab.load(SeqCst) - 1, SeqCst);
|
||||
}
|
||||
|
||||
// update tab content
|
||||
self.tab_content_grid
|
||||
.attach(self.tab_mut().enable()?, 0, 0, 1, 1)?;
|
||||
self.tab().select()?;
|
||||
}
|
||||
true => {
|
||||
self.tab_mut().previous_tab()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CharacterWindow {
|
||||
fn setup_menu(&self) -> Result<()> {
|
||||
// let open_statistics = self.switch_tab(STATISTICS_TAB);
|
||||
// let open_abilities = self.switch_tab(ABILITY_TAB);
|
||||
// let open_inventory = self.switch_tab(INVENTORY_TAB);
|
||||
|
||||
let close = self.close.clone();
|
||||
|
||||
self.menu_gui.set_click_callbacks(
|
||||
ClickCallbacks::default()
|
||||
// .add("open_statistics", open_statistics)
|
||||
// .add("open_abilities", open_abilities)
|
||||
// .add("open_inventory", open_inventory)
|
||||
.add("close", close)
|
||||
.into(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
use crate::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::content::{Content, ContentUpdate, ContentWrapper};
|
||||
|
||||
use super::traits::*;
|
||||
|
||||
pub struct EmptyRightSide;
|
||||
|
||||
impl RightSide for EmptyRightSide {
|
||||
fn refresh(&mut self, _engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PageContent<A: Ability + 'static, T: Send + Sync> {
|
||||
content: Content<A, T>,
|
||||
tooltip: Arc<GuiSnippet>,
|
||||
|
||||
right_side: Box<dyn RightSide>,
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static, T: Send + Sync> PageContent<A, T> {
|
||||
pub fn new<R>(content: Content<A, T>, tooltip: Arc<GuiSnippet>, right_side: R) -> Self
|
||||
where
|
||||
R: RightSide + 'static,
|
||||
{
|
||||
Self {
|
||||
content,
|
||||
tooltip,
|
||||
right_side: Box::new(right_side),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static, T: Send + Sync> PageContentWrapper for PageContent<A, T>
|
||||
where
|
||||
Content<A, T>: ContentUpdate,
|
||||
{
|
||||
fn content(&self) -> &dyn ContentWrapper {
|
||||
&self.content
|
||||
}
|
||||
|
||||
fn content_mut(&mut self) -> &mut dyn ContentWrapper {
|
||||
&mut self.content
|
||||
}
|
||||
|
||||
fn tooltip(&self) -> &Arc<GuiSnippet> {
|
||||
&self.tooltip
|
||||
}
|
||||
|
||||
fn right_side(&self) -> &dyn RightSide {
|
||||
&*self.right_side
|
||||
}
|
||||
|
||||
fn right_side_mut(&mut self) -> &mut dyn RightSide {
|
||||
&mut *self.right_side
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::content::ContentWrapper;
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait PageContentWrapper: Send + Sync {
|
||||
fn content(&self) -> &dyn ContentWrapper;
|
||||
fn content_mut(&mut self) -> &mut dyn ContentWrapper;
|
||||
fn tooltip(&self) -> &Arc<GuiSnippet>;
|
||||
fn right_side(&self) -> &dyn RightSide;
|
||||
fn right_side_mut(&mut self) -> &mut dyn RightSide;
|
||||
}
|
||||
|
||||
pub trait RightSide: Send + Sync {
|
||||
fn refresh(&mut self, engine: &Engine, hero: Entity) -> Result<()>;
|
||||
|
||||
fn disable(&mut self, _engine: &Engine, _hero: Entity) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base(&self) -> &Arc<GuiSnippet>;
|
||||
}
|
|
@ -5,9 +5,7 @@ authors = ["hodasemi <superschneider@t-online.de>"]
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
utilities = { workspace = true }
|
||||
audio = { workspace = true, optional = true }
|
||||
assetpath = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
||||
presentation = { path = "../presentation" }
|
||||
|
|
|
@ -6,8 +6,6 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
# needed
|
||||
destructure_traitobject = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
ron = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
|
@ -20,8 +18,6 @@ assetpath = { workspace = true }
|
|||
shaderc = { workspace = true }
|
||||
|
||||
config_handler = { path = "../ConfigHandler" }
|
||||
lua-wrapper = { path = "../lua-wrapper" }
|
||||
scene_update_macros = { path = "../scene_update_macros" }
|
||||
asset = { path = "../asset" }
|
||||
loading_screen = { path = "../loading-screen" }
|
||||
context = { path = "../context", features = ["bundle_sdl2", "sound"] }
|
||||
|
|
|
@ -14,11 +14,6 @@ pub use crate::engine::{
|
|||
engine_settings::*,
|
||||
};
|
||||
|
||||
pub use serde::{
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
ser::{SerializeMap, SerializeSeq, SerializeStruct},
|
||||
};
|
||||
|
||||
pub use ecs::*;
|
||||
pub use ron;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::prelude::*;
|
|||
|
||||
use asset::GltfBoundingBox as GltfBB;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utilities::prelude::cgmath::Vector3;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use cgmath::{Matrix2, Rad, Vector2, Vector3, Zero};
|
||||
|
||||
use cgmath::Matrix4;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::prelude::*;
|
||||
use anyhow::Result;
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::prelude::*;
|
|||
|
||||
use asset::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utilities::prelude::cgmath::{Deg, InnerSpace, Matrix4, vec3};
|
||||
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::{collections::HashMap, time::Duration};
|
|||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::entityparser::AnimationData;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::fmt;
|
||||
|
||||
use engine::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum EntityTags {
|
||||
|
|
|
@ -4,6 +4,7 @@ use assetpath::AssetPath;
|
|||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::animation_info::AnimationType;
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "map"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
rusqlite = { workspace = true }
|
||||
assetpath = { workspace = true }
|
||||
destructure_traitobject = { workspace = true }
|
||||
|
||||
engine = { path = "../engine" }
|
||||
ecs = { path = "../ecs" }
|
|
@ -1,89 +0,0 @@
|
|||
// engine
|
||||
use engine::prelude::*;
|
||||
|
||||
// std
|
||||
use std::mem;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use super::map_db::MapDataBase;
|
||||
|
||||
// sql
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct AsyncDBAccess {
|
||||
sql: Arc<Mutex<MapDataBase>>,
|
||||
async_thread: RwLock<AsyncThread<Result<()>>>,
|
||||
queue: RwLock<Vec<DispatchCommand>>,
|
||||
}
|
||||
|
||||
impl AsyncDBAccess {
|
||||
pub fn new(map_db: MapDataBase) -> AsyncDBAccess {
|
||||
AsyncDBAccess {
|
||||
sql: Arc::new(Mutex::new(map_db)),
|
||||
async_thread: RwLock::new(AsyncThread::spawn(move || Ok(()))),
|
||||
queue: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add<F>(&self, f: F) -> Result<()>
|
||||
where
|
||||
F: FnOnce(&MapDataBase) -> Result<()> + Send + Sync + 'static,
|
||||
{
|
||||
let sql_clone = self.sql.clone();
|
||||
self.queue
|
||||
.write()
|
||||
.unwrap()
|
||||
.push(DispatchCommand::new(sql_clone, f));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dispatch(&self) -> Result<()> {
|
||||
let mut async_thread = self.async_thread.write().unwrap();
|
||||
let mut queue = self.queue.write().unwrap();
|
||||
|
||||
// check if async thread is returned
|
||||
if async_thread.check()? {
|
||||
// check if there are calls to be dispatched
|
||||
if !queue.is_empty() {
|
||||
// move calls into queue to call it in a thread
|
||||
let mut dispatch_queue = Vec::new();
|
||||
mem::swap(&mut dispatch_queue, &mut queue);
|
||||
|
||||
*async_thread = AsyncThread::spawn(move || {
|
||||
for dispatch_call in dispatch_queue {
|
||||
dispatch_call.call()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for AsyncDBAccess {}
|
||||
|
||||
struct DispatchCommand {
|
||||
sql: Arc<Mutex<MapDataBase>>,
|
||||
command: Box<dyn FnOnce(&MapDataBase) -> Result<()> + Send + Sync>,
|
||||
}
|
||||
|
||||
impl DispatchCommand {
|
||||
pub fn new<F>(sql: Arc<Mutex<MapDataBase>>, command: F) -> Self
|
||||
where
|
||||
F: FnOnce(&MapDataBase) -> Result<()> + Send + Sync + 'static,
|
||||
{
|
||||
DispatchCommand {
|
||||
sql,
|
||||
command: Box::new(command),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(self) -> Result<()> {
|
||||
let sql_lock = self.sql.lock().unwrap();
|
||||
|
||||
(self.command)(&*sql_lock)
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
mod async_db;
|
||||
|
||||
pub mod map;
|
||||
pub mod mapdata;
|
||||
mod surface;
|
||||
mod tile;
|
||||
|
||||
mod map_db;
|
1063
map/src/map.rs
1063
map/src/map.rs
File diff suppressed because it is too large
Load diff
|
@ -1,943 +0,0 @@
|
|||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::cgmath::{Rad, Vector2};
|
||||
|
||||
// sql
|
||||
use rusqlite::{
|
||||
types::{ToSql, Type},
|
||||
Connection, Error,
|
||||
};
|
||||
|
||||
use super::mapdata::Coordinate;
|
||||
|
||||
struct DBMetaInfo {
|
||||
name: String,
|
||||
version: String,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
pub struct DBTextureInfo {
|
||||
pub index: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub struct DBEntityInfo {
|
||||
pub x_tile: u32,
|
||||
pub y_tile: u32,
|
||||
|
||||
pub world: Vector2<f32>,
|
||||
pub rotation: Rad<f32>,
|
||||
|
||||
pub entity: String,
|
||||
}
|
||||
|
||||
pub struct DBNPCSpawnInfo {
|
||||
pub x_tile: u32,
|
||||
pub y_tile: u32,
|
||||
|
||||
pub radius: f32,
|
||||
|
||||
pub min_count: u32,
|
||||
pub max_count: u32,
|
||||
|
||||
pub normal_npc: String,
|
||||
pub elite_npc: String,
|
||||
}
|
||||
|
||||
pub struct DBBossSpawnInfo {
|
||||
pub x_tile: u32,
|
||||
pub y_tile: u32,
|
||||
|
||||
pub boss: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
pub struct EntityDBType {
|
||||
table_name: String,
|
||||
}
|
||||
|
||||
impl EntityDBType {
|
||||
pub fn entity() -> Self {
|
||||
Self {
|
||||
table_name: "entitypositions".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_location() -> Self {
|
||||
Self {
|
||||
table_name: "spawnlocations".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn leave_location() -> Self {
|
||||
Self {
|
||||
table_name: "leavelocations".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MapDBVersions;
|
||||
|
||||
#[allow(unused)]
|
||||
impl MapDBVersions {
|
||||
const VERSION_0_1_0: &'static str = "0.1.0";
|
||||
const VERSION_0_1_1: &'static str = "0.1.1";
|
||||
const VERSION_0_2_0: &'static str = "0.2.0";
|
||||
}
|
||||
|
||||
pub struct MapDataBase {
|
||||
sql: Connection,
|
||||
|
||||
name: String,
|
||||
version: String,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl MapDataBase {
|
||||
pub fn new(path: impl AsRef<Path>, name: &str, width: u32, height: u32) -> Result<Self> {
|
||||
let mut me = Self::open_file(path)?;
|
||||
|
||||
me.create_tables()?;
|
||||
me.init_default_values(name, width, height)?;
|
||||
|
||||
me.read_meta_data()?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let mut me = Self::open_file(path)?;
|
||||
|
||||
me.check_version()?;
|
||||
me.read_meta_data()?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
fn open_file(path: impl AsRef<Path>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sql: Connection::open(path)?,
|
||||
|
||||
name: Default::default(),
|
||||
version: Default::default(),
|
||||
width: 0,
|
||||
height: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MapDataBase {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn width(&self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl MapDataBase {
|
||||
fn init_default_values(&self, name: &str, width: u32, height: u32) -> Result<()> {
|
||||
// meta data
|
||||
self.sql.execute(
|
||||
"INSERT INTO meta (name, version, width, height)
|
||||
VALUES (?1, ?2, ?3, ?4)",
|
||||
&[
|
||||
&name.to_string() as &dyn ToSql,
|
||||
&MapDBVersions::VERSION_0_1_1.to_string() as &dyn ToSql,
|
||||
&width,
|
||||
&height,
|
||||
],
|
||||
)?;
|
||||
|
||||
// tiles
|
||||
let tile_count = width * height;
|
||||
let mut insert_tiles = "INSERT INTO tiles (id, texture) VALUES ".to_string();
|
||||
|
||||
for i in 0..tile_count {
|
||||
if i != 0 {
|
||||
insert_tiles.push(',');
|
||||
}
|
||||
|
||||
insert_tiles.push_str(format!("({}, 1)", i + 1).as_str());
|
||||
}
|
||||
|
||||
self.sql.execute(insert_tiles.as_str(), [])?;
|
||||
|
||||
// heights
|
||||
let height_point_count = (width + 1) * (height + 1);
|
||||
let mut insert_height_points = "INSERT INTO heights (id, height) VALUES".to_string();
|
||||
|
||||
for i in 0..height_point_count {
|
||||
if i != 0 {
|
||||
insert_height_points.push(',');
|
||||
}
|
||||
|
||||
insert_height_points.push_str(format!("({}, 0.0)", i + 1).as_str());
|
||||
}
|
||||
|
||||
self.sql.execute(insert_height_points.as_str(), [])?;
|
||||
|
||||
// textures
|
||||
self.sql.execute(
|
||||
"INSERT INTO textures (id, name) VALUES (1, ?1)",
|
||||
&[&"grass"],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_tables(&self) -> Result<()> {
|
||||
// --------------------------------------------------------
|
||||
// -------- meta information (name, width, height) --------
|
||||
// --------------------------------------------------------
|
||||
self.create_meta_table()?;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// --- tiles information (which tile has which texture) ---
|
||||
// --------------------------------------------------------
|
||||
self.create_tiles_table()?;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// ------------ textures (name of the texture) ------------
|
||||
// --------------------------------------------------------
|
||||
self.create_textures_table()?;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// ----------------------- heights ------------------------
|
||||
// --------------------------------------------------------
|
||||
self.create_heights_table()?;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// ------------------- entity positions -------------------
|
||||
// --------------------------------------------------------
|
||||
self.create_entity_positions_table()?;
|
||||
|
||||
self.create_spawn_table()?;
|
||||
self.create_leave_table()?;
|
||||
self.create_mob_spawn_table()?;
|
||||
self.create_boss_spawn_table()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_meta_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS meta (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
width INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM meta", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_tiles_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS tiles (
|
||||
id INTEGER PRIMARY KEY,
|
||||
texture INTEGER NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM tiles", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_textures_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS textures (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM textures", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_heights_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS heights (
|
||||
id INTEGER PRIMARY KEY,
|
||||
height REAL NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM heights", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_entity_positions_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS entitypositions (
|
||||
x_tile INTEGER NOT NULL,
|
||||
y_tile INTEGER NOT NULL,
|
||||
entity_id TEXT NOT NULL,
|
||||
x_world REAL NOT NULL,
|
||||
y_world REAL NOT NULL,
|
||||
rotation REAL NOT NULL,
|
||||
primary key (x_tile, y_tile)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM entitypositions", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_spawn_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS spawnlocations (
|
||||
x_tile INTEGER NOT NULL,
|
||||
y_tile INTEGER NOT NULL,
|
||||
entity_id TEXT NOT NULL,
|
||||
x_world REAL NOT NULL,
|
||||
y_world REAL NOT NULL,
|
||||
rotation REAL NOT NULL,
|
||||
primary key (x_tile, y_tile)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM spawnlocations", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_leave_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS leavelocations (
|
||||
x_tile INTEGER NOT NULL,
|
||||
y_tile INTEGER NOT NULL,
|
||||
entity_id TEXT NOT NULL,
|
||||
x_world REAL NOT NULL,
|
||||
y_world REAL NOT NULL,
|
||||
rotation REAL NOT NULL,
|
||||
primary key (x_tile, y_tile)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM leavelocations", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_mob_spawn_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS mobspawnlocations (
|
||||
x_tile INTEGER NOT NULL,
|
||||
y_tile INTEGER NOT NULL,
|
||||
radius REAL NOT NULL,
|
||||
normal_npc TEXT,
|
||||
elite_npc TEXT,
|
||||
min_count INTEGER,
|
||||
max_count INTEGER,
|
||||
primary key (x_tile, y_tile)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM mobspawnlocations", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_boss_spawn_table(&self) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS bossspawnlocations (
|
||||
x_tile INTEGER NOT NULL,
|
||||
y_tile INTEGER NOT NULL,
|
||||
boss TEXT,
|
||||
primary key (x_tile, y_tile)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute("DELETE FROM bossspawnlocations", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MapDataBase {
|
||||
fn read_meta_data(&mut self) -> Result<()> {
|
||||
let info = self.sql.query_row(
|
||||
"SELECT name, version, width, height FROM meta WHERE id=1",
|
||||
[],
|
||||
|row| {
|
||||
Ok(DBMetaInfo {
|
||||
name: row.get(0)?,
|
||||
version: row.get(1)?,
|
||||
width: row.get(2)?,
|
||||
height: row.get(3)?,
|
||||
})
|
||||
},
|
||||
)?;
|
||||
|
||||
self.name = info.name;
|
||||
self.version = info.version;
|
||||
self.width = info.width;
|
||||
self.height = info.height;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_tiles(&self) -> Result<HashMap<u32, u32>> {
|
||||
let mut tiles_stmt = self.sql.prepare("SELECT * FROM tiles")?;
|
||||
|
||||
let mapped_tiles = tiles_stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
|
||||
|
||||
let mut tiles = HashMap::new();
|
||||
|
||||
for row in mapped_tiles {
|
||||
let (id, tile_id) = row?;
|
||||
|
||||
tiles.insert(id, tile_id);
|
||||
}
|
||||
|
||||
Ok(tiles)
|
||||
}
|
||||
|
||||
pub fn read_heights(&self) -> Result<HashMap<u32, f64>> {
|
||||
let mut height_stmt = self.sql.prepare("SELECT * FROM heights")?;
|
||||
|
||||
let mapped_heights = height_stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
|
||||
|
||||
let mut heights = HashMap::new();
|
||||
|
||||
for row in mapped_heights {
|
||||
let (id, height) = row?;
|
||||
|
||||
heights.insert(id, height);
|
||||
}
|
||||
|
||||
Ok(heights)
|
||||
}
|
||||
|
||||
pub fn read_textures(&self) -> Result<Vec<DBTextureInfo>> {
|
||||
let mut texture_stmt = self.sql.prepare("SELECT * FROM textures")?;
|
||||
|
||||
let mapped_textures = texture_stmt.query_map([], |row| {
|
||||
Ok(DBTextureInfo {
|
||||
index: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut textures = Vec::new();
|
||||
|
||||
for row in mapped_textures {
|
||||
textures.push(row?);
|
||||
}
|
||||
|
||||
Ok(textures)
|
||||
}
|
||||
|
||||
fn read_ent_pos(&self, table_name: &str) -> Result<Vec<DBEntityInfo>> {
|
||||
let mut entity_pos_stmt = self.sql.prepare(&format!("SELECT * FROM {}", table_name))?;
|
||||
|
||||
let mapped_entity_pos = entity_pos_stmt.query_map([], |row| {
|
||||
Ok(DBEntityInfo {
|
||||
x_tile: row.get(0)?,
|
||||
y_tile: row.get(1)?,
|
||||
world: Vector2::new(row.get(3)?, row.get(4)?),
|
||||
rotation: Rad(row.get(5)?),
|
||||
entity: row.get(2)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut entity_positions = Vec::new();
|
||||
|
||||
for row in mapped_entity_pos {
|
||||
entity_positions.push(row?);
|
||||
}
|
||||
|
||||
Ok(entity_positions)
|
||||
}
|
||||
|
||||
pub fn read_entities(&self) -> Result<Vec<DBEntityInfo>> {
|
||||
self.read_ent_pos("entitypositions")
|
||||
}
|
||||
|
||||
pub fn read_spawn_locations(&self) -> Result<Vec<DBEntityInfo>> {
|
||||
self.read_ent_pos("spawnlocations")
|
||||
}
|
||||
|
||||
pub fn read_leave_locations(&self) -> Result<Vec<DBEntityInfo>> {
|
||||
self.read_ent_pos("leavelocations")
|
||||
}
|
||||
|
||||
pub fn read_mob_spawn_locations(&self) -> Result<Vec<DBNPCSpawnInfo>> {
|
||||
let mut entity_pos_stmt = self.sql.prepare("SELECT * FROM mobspawnlocations")?;
|
||||
|
||||
let mapped_entity_pos = entity_pos_stmt.query_map([], |row| {
|
||||
Ok(DBNPCSpawnInfo {
|
||||
x_tile: row.get(0)?,
|
||||
y_tile: row.get(1)?,
|
||||
radius: row.get(2)?,
|
||||
min_count: row.get(5)?,
|
||||
max_count: row.get(6)?,
|
||||
normal_npc: match row.get(3) {
|
||||
Ok(npc) => npc,
|
||||
Err(err) => match &err {
|
||||
Error::InvalidColumnType(_, _, row_type) => match row_type {
|
||||
Type::Null => String::new(),
|
||||
|
||||
_ => return Err(err),
|
||||
},
|
||||
|
||||
_ => return Err(err),
|
||||
},
|
||||
},
|
||||
elite_npc: match row.get(4) {
|
||||
Ok(npc) => npc,
|
||||
Err(err) => match &err {
|
||||
Error::InvalidColumnType(_, _, row_type) => match row_type {
|
||||
Type::Null => String::new(),
|
||||
|
||||
_ => return Err(err),
|
||||
},
|
||||
|
||||
_ => return Err(err),
|
||||
},
|
||||
},
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut entity_positions = Vec::new();
|
||||
|
||||
for row in mapped_entity_pos {
|
||||
entity_positions.push(row?);
|
||||
}
|
||||
|
||||
Ok(entity_positions)
|
||||
}
|
||||
|
||||
pub fn read_boss_spawn_locations(&self) -> Result<Vec<DBBossSpawnInfo>> {
|
||||
let mut boss_pos_stmt = self.sql.prepare("SELECT * FROM bossspawnlocations")?;
|
||||
|
||||
let mapped_boss_pos = boss_pos_stmt.query_map([], |row| {
|
||||
Ok(DBBossSpawnInfo {
|
||||
x_tile: row.get(0)?,
|
||||
y_tile: row.get(1)?,
|
||||
boss: match row.get(2) {
|
||||
Ok(boss) => boss,
|
||||
Err(err) => match &err {
|
||||
Error::InvalidColumnType(_, _, row_type) => match row_type {
|
||||
Type::Null => String::new(),
|
||||
|
||||
_ => return Err(err),
|
||||
},
|
||||
|
||||
_ => return Err(err),
|
||||
},
|
||||
},
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut boss_positions = Vec::new();
|
||||
|
||||
for row in mapped_boss_pos {
|
||||
boss_positions.push(row?);
|
||||
}
|
||||
|
||||
Ok(boss_positions)
|
||||
}
|
||||
}
|
||||
|
||||
impl MapDataBase {
|
||||
pub fn insert_npc_spawn(
|
||||
&self,
|
||||
tile: (u32, u32),
|
||||
radius: f32,
|
||||
min_count: u32,
|
||||
max_count: u32,
|
||||
) -> Result<()> {
|
||||
let params: &[&dyn ToSql] = &[&tile.0, &tile.1, &(radius as f64), &min_count, &max_count];
|
||||
|
||||
self.sql.execute(
|
||||
"INSERT INTO mobspawnlocations (x_tile, y_tile, radius, min_count, max_count)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
params,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_npc_spawn(&self, tile: (u32, u32)) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"DELETE FROM mobspawnlocations
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&tile.0, &tile.1],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_boss_spawn(&self, tile: (u32, u32)) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"INSERT INTO bossspawnlocations (x_tile, y_tile)
|
||||
VALUES (?1, ?2)",
|
||||
[&tile.0, &tile.1],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_boss_spawn(&self, tile: (u32, u32)) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"DELETE FROM bossspawnlocations
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&tile.0, &tile.1],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_boss_name(&self, coordinate: &Coordinate, name: &str) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE bossspawnlocations
|
||||
SET boss=?3
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&coordinate.x, &coordinate.y, &name as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_entity(
|
||||
&self,
|
||||
entity_db_type: EntityDBType,
|
||||
tile: (u32, u32),
|
||||
entity_name: String,
|
||||
position: Vector2<f32>,
|
||||
rotation: Rad<f32>,
|
||||
) -> Result<()> {
|
||||
self.sql.execute(
|
||||
&format!(
|
||||
"INSERT INTO {} (x_tile, y_tile, entity_id, x_world, y_world, rotation)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||||
entity_db_type.table_name
|
||||
),
|
||||
&[
|
||||
&tile.0,
|
||||
&tile.1,
|
||||
&entity_name as &dyn ToSql,
|
||||
&(position.x as f64),
|
||||
&(position.y as f64),
|
||||
&(rotation.0 as f64),
|
||||
],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_entity(&self, entity_db_type: EntityDBType, tile: (u32, u32)) -> Result<()> {
|
||||
self.sql.execute(
|
||||
&format!(
|
||||
"DELETE FROM {}
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
entity_db_type.table_name
|
||||
),
|
||||
&[&tile.0, &tile.1],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_texture(&self, tile_index: u32, texture_index: u32) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE tiles
|
||||
SET texture=?1
|
||||
WHERE id=?2",
|
||||
&[&texture_index, &tile_index],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_new_texture(&self, texture_index: u32, texture_name: String) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"INSERT INTO textures (id, name)
|
||||
VALUES (?1, ?2)",
|
||||
&[&texture_index, &texture_name as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_texture(&self, texture_index: u32) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"DELETE FROM textures
|
||||
WHERE id=?1",
|
||||
&[&texture_index],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_texture_index(&self, broken_index: u32, last_index: u32) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE tiles
|
||||
SET texture=?1
|
||||
WHERE texture=?2",
|
||||
&[&broken_index, &last_index],
|
||||
)?;
|
||||
|
||||
self.sql.execute(
|
||||
"UPDATE textures
|
||||
SET id=?1
|
||||
WHERE id=?2",
|
||||
&[&broken_index, &last_index],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_height(&self, index: u32, height: f32) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE heights
|
||||
SET height=?1
|
||||
WHERE id=?2",
|
||||
&[&(height as f64), &index as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_npc_spawn_radius(&self, coordinate: &Coordinate, radius: f32) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE mobspawnlocations
|
||||
SET radius=?3
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&coordinate.x, &coordinate.y, &(radius as f64) as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_npc_spawn_min_npc_count(
|
||||
&self,
|
||||
coordinate: &Coordinate,
|
||||
min_npc_count: u32,
|
||||
) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE mobspawnlocations
|
||||
SET min_count=?3
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&coordinate.x, &coordinate.y, &min_npc_count],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_npc_spawn_max_npc_count(
|
||||
&self,
|
||||
coordinate: &Coordinate,
|
||||
max_npc_count: u32,
|
||||
) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE mobspawnlocations
|
||||
SET max_count=?3
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&coordinate.x, &coordinate.y, &max_npc_count],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_npc_spawn_normal_npc(
|
||||
&self,
|
||||
coordinate: &Coordinate,
|
||||
normal_npc: String,
|
||||
) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE mobspawnlocations
|
||||
SET normal_npc=?3
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&coordinate.x, &coordinate.y, &normal_npc as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_npc_spawn_elite_npc(
|
||||
&self,
|
||||
coordinate: &Coordinate,
|
||||
elite_npc: String,
|
||||
) -> Result<()> {
|
||||
self.sql.execute(
|
||||
"UPDATE mobspawnlocations
|
||||
SET elite_npc=?3
|
||||
WHERE x_tile=?1 AND y_tile=?2",
|
||||
&[&coordinate.x, &coordinate.y, &elite_npc as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MapDataBase {
|
||||
fn get_version(&self) -> Result<String, rusqlite::Error> {
|
||||
Ok(
|
||||
match self
|
||||
.sql
|
||||
.query_row("SELECT version FROM meta WHERE id=1", [], |row| row.get(0))
|
||||
{
|
||||
Ok(version) => version,
|
||||
Err(err) => {
|
||||
// everything before version 0.1.1 did not have a version and can be considered as version 0.1.0
|
||||
if let Error::SqliteFailure(_sqlite_error, msg) = &err {
|
||||
if let Some(msg) = msg {
|
||||
if msg.contains("version") {
|
||||
return Ok(MapDBVersions::VERSION_0_1_0.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn check_version(&self) -> Result<()> {
|
||||
let mut version = self.get_version()?;
|
||||
|
||||
// incrementally upgrade to the current version
|
||||
loop {
|
||||
match version.as_str() {
|
||||
MapDBVersions::VERSION_0_1_0 => {
|
||||
// update mob spawn table
|
||||
{
|
||||
// Add new columns to the table
|
||||
self.sql.execute(
|
||||
"ALTER TABLE mobspawnlocations
|
||||
ADD COLUMN radius REAL",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute(
|
||||
"ALTER TABLE mobspawnlocations
|
||||
ADD COLUMN normal_npc TEXT",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute(
|
||||
"ALTER TABLE mobspawnlocations
|
||||
ADD COLUMN elite_npc TEXT",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute(
|
||||
"ALTER TABLE mobspawnlocations
|
||||
ADD COLUMN min_count INTEGER",
|
||||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute(
|
||||
"ALTER TABLE mobspawnlocations
|
||||
ADD COLUMN max_count INTEGER",
|
||||
[],
|
||||
)?;
|
||||
|
||||
// create default values that were used at this time
|
||||
let radius: f32 = 5.0;
|
||||
let min_count: u32 = 3;
|
||||
let max_count: u32 = 7;
|
||||
|
||||
// fill new data
|
||||
self.sql.execute(
|
||||
"UPDATE mobspawnlocations
|
||||
SET
|
||||
radius = ?1,
|
||||
min_count = ?2,
|
||||
max_count = ?3;",
|
||||
&[&radius as &dyn ToSql, &min_count, &max_count],
|
||||
)?;
|
||||
}
|
||||
|
||||
// update meta table, it is possible that version 0.1.0 has no version inside the meta table
|
||||
// thus the old information is read, the table is recreated and the data is inserted back into it
|
||||
{
|
||||
// read old meta data table
|
||||
let (name, width, height): (String, u32, u32) = self.sql.query_row(
|
||||
"SELECT name, width, height FROM meta WHERE id=1",
|
||||
[],
|
||||
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
||||
)?;
|
||||
|
||||
// remove old meta table
|
||||
self.sql.execute("DROP TABLE meta", [])?;
|
||||
|
||||
// create new meta table
|
||||
self.create_meta_table()?;
|
||||
|
||||
// insert meta information back into table
|
||||
self.sql.execute(
|
||||
"INSERT INTO meta (name, version, width, height)
|
||||
VALUES (?1, ?2, ?3, ?4)",
|
||||
&[
|
||||
&name,
|
||||
&MapDBVersions::VERSION_0_1_1.to_string() as &dyn ToSql,
|
||||
&width,
|
||||
&height,
|
||||
],
|
||||
)?;
|
||||
}
|
||||
|
||||
// update version string
|
||||
version = MapDBVersions::VERSION_0_1_1.to_string();
|
||||
}
|
||||
|
||||
MapDBVersions::VERSION_0_1_1 => {
|
||||
// add boss spawn table
|
||||
self.create_boss_spawn_table()?;
|
||||
|
||||
// update version in meta table
|
||||
self.sql.execute(
|
||||
"UPDATE meta
|
||||
SET
|
||||
version = ?1;",
|
||||
[&MapDBVersions::VERSION_0_2_0.to_string() as &dyn ToSql],
|
||||
)?;
|
||||
|
||||
// update version string
|
||||
version = MapDBVersions::VERSION_0_2_0.to_string();
|
||||
}
|
||||
|
||||
// break at the current version
|
||||
MapDBVersions::VERSION_0_2_0 => {
|
||||
break;
|
||||
}
|
||||
|
||||
_ => unreachable!("version string {} not known", version),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
1202
map/src/mapdata.rs
1202
map/src/mapdata.rs
File diff suppressed because it is too large
Load diff
|
@ -1,113 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Surface {
|
||||
point_cloud: Vec<Vec<cgmath::Vector3<f32>>>,
|
||||
|
||||
texture_ids: Vec<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
pub fn new(
|
||||
width: u32,
|
||||
height: u32,
|
||||
point_cloud: Vec<Vec<cgmath::Vector3<f32>>>,
|
||||
tiles: HashMap<u32, u32>,
|
||||
) -> Result<Surface> {
|
||||
let texture_ids = {
|
||||
let mut texture_ids = Vec::with_capacity(width as usize);
|
||||
|
||||
for x in 0..width {
|
||||
let mut tmp_y_ids = Vec::with_capacity(height as usize);
|
||||
|
||||
for y in 0..height {
|
||||
let index = (x + 1) + (y * width);
|
||||
|
||||
// index - 1 since sql starts indices at 1
|
||||
let texture_id = match tiles.get(&index) {
|
||||
Some(tex_id) => tex_id - 1,
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Index ({}) in tiles_map (Surface) not found",
|
||||
index
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
tmp_y_ids.push(texture_id);
|
||||
}
|
||||
|
||||
texture_ids.push(tmp_y_ids);
|
||||
}
|
||||
|
||||
texture_ids
|
||||
};
|
||||
|
||||
Ok(Surface {
|
||||
point_cloud,
|
||||
|
||||
texture_ids,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn texture_id(&self, x: u32, y: u32) -> u32 {
|
||||
self.texture_ids[x as usize][y as usize]
|
||||
}
|
||||
|
||||
pub fn set_texture_id(&mut self, x: u32, y: u32, texture_id: u32) {
|
||||
self.texture_ids[x as usize][y as usize] = texture_id;
|
||||
}
|
||||
|
||||
pub fn point(&self, x: u32, y: u32) -> cgmath::Vector3<f32> {
|
||||
self.point_cloud[x as usize][y as usize]
|
||||
}
|
||||
|
||||
pub fn change_height(&mut self, x: u32, y: u32, height: f32) -> f32 {
|
||||
self.point_cloud[x as usize][y as usize].z += height;
|
||||
self.point_cloud[x as usize][y as usize].z
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn check_plain_2_fixed(
|
||||
&mut self,
|
||||
x1_fixed: u32,
|
||||
y1_fixed: u32,
|
||||
x2_fixed: u32,
|
||||
y2_fixed: u32,
|
||||
) {
|
||||
let ux1 = x1_fixed as usize;
|
||||
let uy1 = y1_fixed as usize;
|
||||
let ux2 = x2_fixed as usize;
|
||||
let uy2 = y2_fixed as usize;
|
||||
|
||||
// fixed points
|
||||
let bp1 = self.point_cloud[ux1][uy1].try_borrow()?;
|
||||
let p1 = bp1.deref();
|
||||
let bp2 = self.point_cloud[ux2][uy2].try_borrow()?;
|
||||
let p2 = bp2.deref();
|
||||
|
||||
// points that need to be checked
|
||||
let bp3 = self.point_cloud[ux1][uy2].try_borrow()?;
|
||||
let p3 = bp3.deref();
|
||||
let bp4 = self.point_cloud[ux2][uy1].try_borrow()?;
|
||||
let p4 = bp4.deref();
|
||||
|
||||
let p12 = p1 - p2;
|
||||
let p13 = p1 - p3;
|
||||
let p14 = p1 - p4;
|
||||
|
||||
let cross = p12.cross(p13);
|
||||
|
||||
let dot = cgmath::dot(cross, p14);
|
||||
|
||||
if dot == 0.0 {
|
||||
// everything is alright
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: elevate p3 and p4 to correct height
|
||||
}
|
||||
*/
|
||||
}
|
191
map/src/tile.rs
191
map/src/tile.rs
|
@ -1,191 +0,0 @@
|
|||
use engine::prelude::cgmath::{Vector2, Vector3};
|
||||
|
||||
use super::{mapdata::Coordinate, surface::Surface};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SlopeDirection {
|
||||
LeftToRight,
|
||||
RightToLeft,
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
DiagonalBottomLeftToTopRight,
|
||||
DiagonalTopLeftToBottomRight,
|
||||
DiagonalTopRightToBottomLeft,
|
||||
DiagonalBottomRightToTopLeft,
|
||||
}
|
||||
|
||||
impl SlopeDirection {
|
||||
pub fn from_direction(direction: Vector2<f32>) -> SlopeDirection {
|
||||
if direction.x < 0.0 {
|
||||
if direction.y < 0.0 {
|
||||
if Self::almost_eq(direction.x, direction.y) {
|
||||
SlopeDirection::DiagonalTopRightToBottomLeft
|
||||
} else if direction.x < direction.y {
|
||||
SlopeDirection::RightToLeft
|
||||
} else {
|
||||
SlopeDirection::TopToBottom
|
||||
}
|
||||
} else if Self::almost_eq(direction.x.abs(), direction.y) {
|
||||
SlopeDirection::DiagonalBottomRightToTopLeft
|
||||
} else if direction.x.abs() < direction.y {
|
||||
SlopeDirection::BottomToTop
|
||||
} else {
|
||||
SlopeDirection::RightToLeft
|
||||
}
|
||||
} else if direction.y < 0.0 {
|
||||
if Self::almost_eq(direction.x, direction.y.abs()) {
|
||||
SlopeDirection::DiagonalTopLeftToBottomRight
|
||||
} else if direction.x < direction.y.abs() {
|
||||
SlopeDirection::TopToBottom
|
||||
} else {
|
||||
SlopeDirection::LeftToRight
|
||||
}
|
||||
} else if Self::almost_eq(direction.x, direction.y) {
|
||||
SlopeDirection::DiagonalBottomLeftToTopRight
|
||||
} else if direction.x < direction.y {
|
||||
SlopeDirection::BottomToTop
|
||||
} else {
|
||||
SlopeDirection::LeftToRight
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn almost_eq(f1: f32, f2: f32) -> bool {
|
||||
let epsilon = 0.001;
|
||||
|
||||
(f1 + epsilon > f2) && (f1 - epsilon < f2)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tile {
|
||||
pub left_bottom: Vector3<f32>,
|
||||
pub right_bottom: Vector3<f32>,
|
||||
pub left_top: Vector3<f32>,
|
||||
pub right_top: Vector3<f32>,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
pub fn new(base_coord: Coordinate, surface: &Surface) -> Tile {
|
||||
Tile {
|
||||
left_bottom: surface.point(base_coord.x, base_coord.y),
|
||||
right_bottom: surface.point(base_coord.x + 1, base_coord.y),
|
||||
left_top: surface.point(base_coord.x, base_coord.y + 1),
|
||||
right_top: surface.point(base_coord.x + 1, base_coord.y + 1),
|
||||
}
|
||||
}
|
||||
|
||||
/// checks for the slope in given direction with given threshold
|
||||
///
|
||||
/// returns `true` when slope is below threshold, `false` otherwise
|
||||
pub fn check_slope(
|
||||
&self,
|
||||
threshold: f32,
|
||||
direction: SlopeDirection,
|
||||
increase_only: bool,
|
||||
) -> bool {
|
||||
match direction {
|
||||
SlopeDirection::LeftToRight => {
|
||||
let bottom_difference = self.left_bottom.z - self.right_bottom.z;
|
||||
|
||||
// check bottom slope against threshold
|
||||
if !Self::check_directed_slope(bottom_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let top_difference = self.left_top.z - self.right_top.z;
|
||||
|
||||
//check top slope against threshold
|
||||
if !Self::check_directed_slope(top_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::RightToLeft => {
|
||||
let bottom_difference = self.right_bottom.z - self.left_bottom.z;
|
||||
|
||||
if !Self::check_directed_slope(bottom_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let top_difference = self.right_top.z - self.left_top.z;
|
||||
|
||||
if !Self::check_directed_slope(top_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::BottomToTop => {
|
||||
let left_difference = self.left_bottom.z - self.left_top.z;
|
||||
|
||||
if !Self::check_directed_slope(left_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let right_difference = self.right_bottom.z - self.right_top.z;
|
||||
|
||||
if !Self::check_directed_slope(right_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::TopToBottom => {
|
||||
let left_difference = self.left_top.z - self.left_bottom.z;
|
||||
|
||||
if !Self::check_directed_slope(left_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let right_difference = self.right_top.z - self.right_bottom.z;
|
||||
|
||||
if !Self::check_directed_slope(right_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::DiagonalBottomLeftToTopRight => {
|
||||
let diagonal_difference = self.left_bottom.z - self.right_top.z;
|
||||
|
||||
if !Self::check_directed_slope(diagonal_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::DiagonalBottomRightToTopLeft => {
|
||||
let diagonal_difference = self.right_bottom.z - self.left_top.z;
|
||||
|
||||
if !Self::check_directed_slope(diagonal_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::DiagonalTopLeftToBottomRight => {
|
||||
let diagonal_difference = self.left_top.z - self.right_bottom.z;
|
||||
|
||||
if !Self::check_directed_slope(diagonal_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SlopeDirection::DiagonalTopRightToBottomLeft => {
|
||||
let diagonal_difference = self.right_top.z - self.left_bottom.z;
|
||||
|
||||
if !Self::check_directed_slope(diagonal_difference, threshold, increase_only) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_directed_slope(difference: f32, threshold: f32, increase_only: bool) -> bool {
|
||||
// check slope against threshold
|
||||
if difference.abs() > threshold {
|
||||
// if increase only, check for increase
|
||||
if increase_only {
|
||||
if difference < 0.0 {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
[package]
|
||||
name = "rpg_components"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
assetpath = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
engine = { path = "../engine" }
|
|
@ -1,20 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="addon_grid" x_dim="1" y_dim="3" padding="4" margin="2"
|
||||
x_offset="0" y_offset="0" vert_align="top" hori_align="left"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
width="300" height="120">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="3" y_dim="1">
|
||||
<icon id="addon_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label x_slot="1" y_slot="0" text_color="black">Addon</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="2" y_dim="1" padding="3" margin="3">
|
||||
<label id="type" x_slot="0" y_slot="0" text_color="black">Type</label>
|
||||
<label id="value" x_slot="1" y_slot="0" text_color="black">Value</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" y_dim="4" margin="2" padding="4" x_offset="0" y_offset="0" vert_align="top" hori_align="left"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
width="300" height="160">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="5" y_dim="1">
|
||||
<icon id="ability_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="rarity_label" x_slot="1" y_slot="0" x_size="2" text_color="black" text_alignment="left">Ability Name</label>
|
||||
<label id="level" x_slot="3" y_slot="0" x_size="2" text_color="black" text_alignment="left">Lvl: 1</label>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="1" x_dim="5" y_dim="1">
|
||||
<label id="ability_name" x_slot="0" y_slot="0" x_size="3" text_color="black" text_alignment="left">Rarity</label>
|
||||
<label id="slot_info" x_slot="3" y_slot="0" x_size="2" text_color="black" text_alignment="left">SlotInfo</label>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="2" y_dim="1">
|
||||
<label id="mana_costs" x_slot="0" y_slot="0" text_color="blue">1</label>
|
||||
<label id="damage" x_slot="1" y_slot="0" text_color="black">62</label>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="3" x_dim="2" y_dim="1">
|
||||
<label x_slot="0" y_slot="0" text_color="black">Cooldown</label>
|
||||
<label id="cooldown" x_slot="1" y_slot="0" text_color="black">3.0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
Binary file not shown.
Before ![]() (image error) Size: 11 KiB |
|
@ -1,248 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<!-- definition of attributes -->
|
||||
<xs:attribute name="id" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="x_slot" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="y_slot" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="x_dim" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="y_dim" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="x_size" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="y_size" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="normal" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="selected" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="click_sound" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="hover_sound" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="select_mode">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="none"></xs:enumeration>
|
||||
<xs:enumeration value="bigger"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="icon" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="icon_margin" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="text_color" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="text_ratio" type="xs:decimal"></xs:attribute>
|
||||
<xs:attribute name="text_alignment">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="left"></xs:enumeration>
|
||||
<xs:enumeration value="right"></xs:enumeration>
|
||||
<xs:enumeration value="top"></xs:enumeration>
|
||||
<xs:enumeration value="bottom"></xs:enumeration>
|
||||
<xs:enumeration value="center"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="select" type="xs:boolean"></xs:attribute>
|
||||
<xs:attribute name="isolate" type="xs:boolean"></xs:attribute>
|
||||
<xs:attribute name="x_offset" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="y_offset" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="width" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="height" type="xs:integer"></xs:attribute>
|
||||
<xs:attribute name="padding" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="margin" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="vert_align">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="top"></xs:enumeration>
|
||||
<xs:enumeration value="middle"></xs:enumeration>
|
||||
<xs:enumeration value="bottom"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="hori_align">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="left"></xs:enumeration>
|
||||
<xs:enumeration value="middle"></xs:enumeration>
|
||||
<xs:enumeration value="right"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="background" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="foreground" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="button_normal" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="button_selected" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="fill_type">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="expand"></xs:enumeration>
|
||||
<xs:enumeration value="square"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="reference_width" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="reference_height" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="layer" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="line_count" type="xs:nonNegativeInteger"></xs:attribute>
|
||||
<xs:attribute name="west_neighbour" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="east_neighbour" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="north_neighbour" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="south_neighbour" type="xs:string"></xs:attribute>
|
||||
|
||||
<!-- definition of complex elements -->
|
||||
<xs:element name="root">
|
||||
<xs:complexType>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element name="grid" />
|
||||
</xs:choice>
|
||||
<xs:attribute ref="reference_width"></xs:attribute>
|
||||
<xs:attribute ref="reference_height"></xs:attribute>
|
||||
<xs:attribute ref="layer"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="grid">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="grid" />
|
||||
<xs:element name="button" />
|
||||
<xs:element name="label" />
|
||||
<xs:element name="progressbar" />
|
||||
<xs:element name="textfield" />
|
||||
<xs:element name="icon" />
|
||||
</xs:choice>
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_dim"></xs:attribute>
|
||||
<xs:attribute ref="y_dim"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="x_offset"></xs:attribute>
|
||||
<xs:attribute ref="y_offset"></xs:attribute>
|
||||
<xs:attribute ref="width"></xs:attribute>
|
||||
<xs:attribute ref="height"></xs:attribute>
|
||||
<xs:attribute ref="padding"></xs:attribute>
|
||||
<xs:attribute ref="margin"></xs:attribute>
|
||||
<xs:attribute ref="vert_align"></xs:attribute>
|
||||
<xs:attribute ref="hori_align"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="button_normal"></xs:attribute>
|
||||
<xs:attribute ref="button_selected"></xs:attribute>
|
||||
<xs:attribute ref="click_sound"></xs:attribute>
|
||||
<xs:attribute ref="hover_sound"></xs:attribute>
|
||||
<xs:attribute ref="layer"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="button">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="select"></xs:attribute>
|
||||
<xs:attribute ref="isolate"></xs:attribute>
|
||||
<xs:attribute ref="normal"></xs:attribute>
|
||||
<xs:attribute ref="selected"></xs:attribute>
|
||||
<xs:attribute ref="fill_type"></xs:attribute>
|
||||
<xs:attribute ref="click_sound"></xs:attribute>
|
||||
<xs:attribute ref="hover_sound"></xs:attribute>
|
||||
<xs:attribute ref="select_mode"></xs:attribute>
|
||||
<xs:attribute ref="icon"></xs:attribute>
|
||||
<xs:attribute ref="icon_margin"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="west_neighbour"></xs:attribute>
|
||||
<xs:attribute ref="east_neighbour"></xs:attribute>
|
||||
<xs:attribute ref="north_neighbour"></xs:attribute>
|
||||
<xs:attribute ref="south_neighbour"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="label">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="multi_line_label">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="line_count"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="multi_line_textfield">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="line_count"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="progressbar">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="foreground"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="textfield">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="icon">
|
||||
<xs:complexType>
|
||||
<xs:attribute ref="id"></xs:attribute>
|
||||
<xs:attribute ref="x_slot"></xs:attribute>
|
||||
<xs:attribute ref="y_slot"></xs:attribute>
|
||||
<xs:attribute ref="x_size"></xs:attribute>
|
||||
<xs:attribute ref="y_size"></xs:attribute>
|
||||
<xs:attribute ref="icon"></xs:attribute>
|
||||
<xs:attribute ref="margin"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="fill_type"></xs:attribute>
|
||||
<xs:attribute ref="text_color"></xs:attribute>
|
||||
<xs:attribute ref="text_ratio"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="4" y_dim="1">
|
||||
<icon id="socket_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="stat_type" x_slot="1" y_slot="0" x_size="3" text_color="black" text_alignment="left">StatType</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,21 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
hori_align="left" y_dim="3" width="300" height="120">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="4" height="160">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="5" height="200">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="6" height="240">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="7" height="280">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="8" height="320">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="9" height="360">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="item_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top"
|
||||
hori_align="left" width="300" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
y_dim="10" height="400">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="6" y_dim="1">
|
||||
<icon id="item_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="slot_label" x_slot="1" y_slot="0" x_size="3" text_color="black">Slot</label>
|
||||
<label id="level_label" x_slot="4" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<label id="rarity_label" x_slot="0" y_slot="1">Rarity</label>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="3" y_dim="1" padding="3" margin="3">
|
||||
<label x_slot="0" y_slot="0" text_color="#C23519" id="strength_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="1" y_slot="0" text_color="#65D01E" id="agility_field" text_ratio="0.9">0</label>
|
||||
<label x_slot="2" y_slot="0" text_color="#1D63B3" id="intelligence_field" text_ratio="0.9">0</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="1280" reference_height="720" layer="11">
|
||||
<grid id="main_grid" x_dim="1" x_offset="0" y_offset="0" vert_align="top" padding="4" margin="2"
|
||||
background='{"background_color":"#794c0a","border_color":"#543919","border_thickness":{"Pixel":2}}'
|
||||
hori_align="left" y_dim="3" width="300" height="120">
|
||||
<!-- header -->
|
||||
<grid x_slot="0" y_slot="0" x_dim="3" y_dim="1">
|
||||
<icon id="jewel_icon" x_slot="0" y_slot="0"></icon>
|
||||
<label id="rarity_label" x_slot="1" y_slot="0" x_size="2" text_color="black">Level</label>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="1" x_dim="4" y_dim="1" padding="3" margin="3">
|
||||
<label id="attribute_type" x_slot="0" y_slot="0" x_size="3" text_color="black">Attribute</label>
|
||||
<label id="attribute_value" x_slot="3" y_slot="0" text_color="black">Value</label>
|
||||
</grid>
|
||||
|
||||
<grid x_slot="0" y_slot="2" x_dim="4" y_dim="1" padding="3" margin="3">
|
||||
<label id="stat_type" x_slot="0" y_slot="0" x_size="3" text_color="black">Stat</label>
|
||||
<label id="stat_value" x_slot="3" y_slot="0" text_color="black">Value</label>
|
||||
</grid>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root>
|
||||
<grid x_dim="3" y_dim="1">
|
||||
<label id="stat_type" x_slot="0" y_slot="0" x_size="2" text_color="black" text_alignment="left">StatType</label>
|
||||
<label id="stat_value" x_slot="2" y_slot="0" text_color="black" text_alignment="right">StatValue</label>
|
||||
</grid>
|
||||
</root>
|
|
@ -1,169 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use cgmath::{Vector2, Zero};
|
||||
use engine::prelude::*;
|
||||
use paste;
|
||||
|
||||
use std::{
|
||||
slice::{Iter, IterMut},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use crate::{components::inventory::Storable, items::ItemSystem};
|
||||
use crate::{config::save_game::SaveGame, items::ability_book::Ability};
|
||||
use crate::{
|
||||
damage_type::DamageType,
|
||||
items::{ability_addon::AbilityAddonTypes, ability_book::AbilityBook, Rarities},
|
||||
};
|
||||
|
||||
use super::{character_status::CharacterStatus, statistics::Statistics};
|
||||
|
||||
macro_rules! load {
|
||||
($me: ident, $item_system:ident, $save_game:ident, $($index:literal,)+) => {
|
||||
paste::expr! {
|
||||
$(
|
||||
if $save_game.[<ability_ $index>].used {
|
||||
let ability = &$save_game.[<ability_ $index>];
|
||||
|
||||
let mut addons = Vec::new();
|
||||
|
||||
for addon in ability.addons.iter() {
|
||||
let mut split = addon.split('|');
|
||||
|
||||
let rarity = Rarities::from_str(&split.nth(0).unwrap())?;
|
||||
let addon_type = AbilityAddonTypes::from_str(&split.nth(0).unwrap())?;
|
||||
|
||||
addons.push(Some($item_system.addon(rarity, addon_type)));
|
||||
}
|
||||
|
||||
let book = $item_system.ability_book(
|
||||
&ability.name,
|
||||
ability.rarity,
|
||||
addons,
|
||||
ability.level,
|
||||
);
|
||||
|
||||
$me.abilities[$index] = Some(book);
|
||||
}
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! store {
|
||||
($me: ident, $save_game:ident, $($index:literal,)+) => {
|
||||
paste::expr! {
|
||||
$(
|
||||
if let Some(book) = &$me.abilities[$index] {
|
||||
let ability = &mut $save_game.[<ability_ $index>];
|
||||
|
||||
ability.used = true;
|
||||
ability.name = book.ability().name().to_string();
|
||||
ability.rarity = book.rarity();
|
||||
ability.level = book.level();
|
||||
|
||||
for addon in book.addons().iter() {
|
||||
if let Some(addon) = addon {
|
||||
ability.addons.push(format!("{}|{}", addon.rarity(), addon.addon_type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// stupid workaround for serde Deserialize
|
||||
pub const MAX_ABILITIES: usize = 4;
|
||||
|
||||
pub struct AbilitySlots<A: Ability> {
|
||||
pub direction: Vector2<f32>,
|
||||
|
||||
abilities: [Option<AbilityBook<A>>; MAX_ABILITIES],
|
||||
}
|
||||
|
||||
impl<A: Ability> AbilitySlots<A> {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
direction: Vector2::zero(),
|
||||
abilities: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(item_system: &ItemSystem<A>, save_game: &SaveGame) -> Result<Self> {
|
||||
let mut me = Self::empty();
|
||||
|
||||
load!(me, item_system, save_game, 0, 1, 2, 3,);
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
pub fn store(&self, save_game: &mut SaveGame) {
|
||||
store!(self, save_game, 0, 1, 2, 3,);
|
||||
}
|
||||
|
||||
pub fn insert_book(&mut self, book: AbilityBook<A>, index: usize) -> Option<AbilityBook<A>> {
|
||||
match self.abilities[index].clone() {
|
||||
Some(ability) => {
|
||||
if ability != book {
|
||||
self.abilities[index] = Some(book);
|
||||
} else {
|
||||
self.abilities[index] = Some(book);
|
||||
}
|
||||
|
||||
Some(ability)
|
||||
}
|
||||
None => {
|
||||
self.abilities[index] = Some(book);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn clear_book(&mut self, index: usize) {
|
||||
// self.abilities[index] = None;
|
||||
// }
|
||||
|
||||
pub fn book(&self, index: usize) -> Option<&AbilityBook<A>> {
|
||||
self.abilities[index].as_ref()
|
||||
}
|
||||
|
||||
pub fn book_mut(&mut self, index: usize) -> Option<&mut AbilityBook<A>> {
|
||||
self.abilities[index].as_mut()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_, Option<AbilityBook<A>>> {
|
||||
self.abilities.iter()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, Option<AbilityBook<A>>> {
|
||||
self.abilities.iter_mut()
|
||||
}
|
||||
|
||||
pub fn apply_damage(
|
||||
base_damage: u32,
|
||||
damage_type: DamageType,
|
||||
enemy_statistics: &Statistics,
|
||||
enemy_status: &mut CharacterStatus,
|
||||
) {
|
||||
let resistance = enemy_statistics.calculate_resistance(damage_type);
|
||||
|
||||
if resistance < base_damage {
|
||||
let damage = base_damage - resistance;
|
||||
|
||||
enemy_status.apply_damage(damage as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> EntityComponent for AbilitySlots<A> {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability> ComponentDebug for AbilitySlots<A> {
|
||||
fn debug_name() -> &'static str {
|
||||
"AbilitySlots"
|
||||
}
|
||||
}
|
|
@ -1,325 +0,0 @@
|
|||
use anyhow::bail;
|
||||
use engine::prelude::{image::RgbaImage, *};
|
||||
|
||||
use std::{cmp::Ordering, slice::Iter, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
config::attributes::{AttributeColorSettings, StartingAttributes},
|
||||
items::{ability_book::Ability, ItemSystem},
|
||||
};
|
||||
|
||||
generate_stat!(Agility, u32, "Agility");
|
||||
generate_stat!(Intelligence, u32, "Intelligence");
|
||||
generate_stat!(Strength, u32, "Strength");
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd)]
|
||||
pub enum Attribute {
|
||||
Agility,
|
||||
Intelligence,
|
||||
Strength,
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub fn random() -> Self {
|
||||
match Random::range(0, 3) {
|
||||
0 => Self::Agility,
|
||||
1 => Self::Intelligence,
|
||||
2 => Self::Strength,
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter() -> Iter<'static, Self> {
|
||||
use Attribute::*;
|
||||
|
||||
static ATTRIBUTES: [Attribute; 3] = [Agility, Intelligence, Strength];
|
||||
ATTRIBUTES.iter()
|
||||
}
|
||||
|
||||
pub fn apply_color<A: Ability>(
|
||||
&self,
|
||||
base_image: &RgbaImage,
|
||||
color_settins: &AttributeColorSettings,
|
||||
) -> RgbaImage {
|
||||
ItemSystem::<A>::apply_color(base_image, color_settins.from_attribute(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Attribute {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"agility" => Ok(Self::Agility),
|
||||
"intelligence" => Ok(Self::Intelligence),
|
||||
"strength" => Ok(Self::Strength),
|
||||
|
||||
_ => bail!("could not parse attribute from {s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Attribute {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Attribute::Agility => write!(f, "agility"),
|
||||
Attribute::Intelligence => write!(f, "intelligence"),
|
||||
Attribute::Strength => write!(f, "strength"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Attributes {
|
||||
agility: Agility,
|
||||
intelligence: Intelligence,
|
||||
strength: Strength,
|
||||
|
||||
pub bonus_agility: Agility,
|
||||
pub bonus_intelligence: Intelligence,
|
||||
pub bonus_strength: Strength,
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
pub fn zero() -> Self {
|
||||
Attributes {
|
||||
agility: Agility::default(),
|
||||
intelligence: Intelligence::default(),
|
||||
strength: Strength::default(),
|
||||
|
||||
bonus_agility: Agility::default(),
|
||||
bonus_intelligence: Intelligence::default(),
|
||||
bonus_strength: Strength::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(starting_attributes: &StartingAttributes) -> Self {
|
||||
Attributes {
|
||||
agility: Agility::from(starting_attributes.agility),
|
||||
intelligence: Intelligence::from(starting_attributes.intelligence),
|
||||
strength: Strength::from(starting_attributes.strength),
|
||||
|
||||
bonus_agility: Agility::default(),
|
||||
bonus_intelligence: Intelligence::default(),
|
||||
bonus_strength: Strength::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(strength: u32, agility: u32, intelligence: u32) -> Self {
|
||||
Attributes {
|
||||
agility: Agility::from(agility),
|
||||
intelligence: Intelligence::from(intelligence),
|
||||
strength: Strength::from(strength),
|
||||
|
||||
bonus_agility: Agility::default(),
|
||||
bonus_intelligence: Intelligence::default(),
|
||||
bonus_strength: Strength::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_boni(&mut self, strength: Strength, agility: Agility, intelligence: Intelligence) {
|
||||
self.strength = strength;
|
||||
self.agility = agility;
|
||||
self.intelligence = intelligence;
|
||||
}
|
||||
|
||||
pub fn random_with_base(sum: u32, starting_attributes: &StartingAttributes) -> Self {
|
||||
let me = Self::new(starting_attributes);
|
||||
let mut random = Self::zero();
|
||||
random.randomize(sum);
|
||||
|
||||
me + random
|
||||
}
|
||||
|
||||
pub fn agility(&self) -> Agility {
|
||||
self.agility + self.bonus_agility
|
||||
}
|
||||
|
||||
pub fn strength(&self) -> Strength {
|
||||
self.strength + self.bonus_strength
|
||||
}
|
||||
|
||||
pub fn intelligence(&self) -> Intelligence {
|
||||
self.intelligence + self.bonus_intelligence
|
||||
}
|
||||
|
||||
pub fn base_agility(&self) -> Agility {
|
||||
self.agility
|
||||
}
|
||||
|
||||
pub fn base_strength(&self) -> Strength {
|
||||
self.strength
|
||||
}
|
||||
|
||||
pub fn base_intelligence(&self) -> Intelligence {
|
||||
self.intelligence
|
||||
}
|
||||
|
||||
pub fn add_agility(&mut self, agility: Agility) {
|
||||
self.agility += agility;
|
||||
}
|
||||
|
||||
pub fn add_strength(&mut self, strength: Strength) {
|
||||
self.strength += strength;
|
||||
}
|
||||
|
||||
pub fn add_intelligence(&mut self, intelligence: Intelligence) {
|
||||
self.intelligence += intelligence;
|
||||
}
|
||||
|
||||
pub fn base_sum(&self) -> u32 {
|
||||
self.agility.raw() + self.intelligence.raw() + self.strength.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
pub fn randomize(&mut self, sum: u32) {
|
||||
enum AttributeTypes {
|
||||
Strength,
|
||||
Agility,
|
||||
Intelligence,
|
||||
}
|
||||
|
||||
let mut attrs = vec![
|
||||
AttributeTypes::Strength,
|
||||
AttributeTypes::Agility,
|
||||
AttributeTypes::Intelligence,
|
||||
];
|
||||
|
||||
let n = Random::range(0, 3) as usize;
|
||||
let attr_type = attrs.remove(n);
|
||||
|
||||
let val1 = match attr_type {
|
||||
AttributeTypes::Strength => {
|
||||
self.strength.set(Random::range(0, sum + 1));
|
||||
self.strength.raw()
|
||||
}
|
||||
AttributeTypes::Agility => {
|
||||
self.agility.set(Random::range(0, sum + 1));
|
||||
self.agility.raw()
|
||||
}
|
||||
AttributeTypes::Intelligence => {
|
||||
self.intelligence.set(Random::range(0, sum + 1));
|
||||
self.intelligence.raw()
|
||||
}
|
||||
};
|
||||
|
||||
let n = Random::range(0, 2) as usize;
|
||||
let attr_type = attrs.remove(n);
|
||||
|
||||
let val2 = match attr_type {
|
||||
AttributeTypes::Strength => {
|
||||
self.strength.set(Random::range(0, (sum - val1) + 1));
|
||||
self.strength.raw()
|
||||
}
|
||||
AttributeTypes::Agility => {
|
||||
self.agility.set(Random::range(0, (sum - val1) + 1));
|
||||
self.agility.raw()
|
||||
}
|
||||
AttributeTypes::Intelligence => {
|
||||
self.intelligence.set(Random::range(0, (sum - val1) + 1));
|
||||
self.intelligence.raw()
|
||||
}
|
||||
};
|
||||
|
||||
let attr_type = attrs.remove(0);
|
||||
|
||||
match attr_type {
|
||||
AttributeTypes::Strength => {
|
||||
self.strength.set(sum - (val1 + val2));
|
||||
self.strength.raw()
|
||||
}
|
||||
AttributeTypes::Agility => {
|
||||
self.agility.set(sum - (val1 + val2));
|
||||
self.agility.raw()
|
||||
}
|
||||
AttributeTypes::Intelligence => {
|
||||
self.intelligence.set(sum - (val1 + val2));
|
||||
self.intelligence.raw()
|
||||
}
|
||||
};
|
||||
|
||||
debug_assert!(sum == self.sum());
|
||||
}
|
||||
|
||||
pub fn sum(&self) -> u32 {
|
||||
self.agility.raw() + self.intelligence.raw() + self.strength.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Attributes {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Attributes) -> Self {
|
||||
Attributes {
|
||||
agility: self.agility + other.agility,
|
||||
strength: self.strength + other.strength,
|
||||
intelligence: self.intelligence + other.intelligence,
|
||||
|
||||
bonus_agility: Agility::default(),
|
||||
bonus_intelligence: Intelligence::default(),
|
||||
bonus_strength: Strength::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Attributes {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.intelligence == other.intelligence
|
||||
&& self.agility == other.agility
|
||||
&& self.strength == other.strength
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Attributes {}
|
||||
|
||||
impl PartialOrd for Attributes {
|
||||
fn partial_cmp(&self, other: &Attributes) -> Option<Ordering> {
|
||||
let strength = self.strength.raw().cmp(&other.strength.raw());
|
||||
|
||||
if strength == Ordering::Less {
|
||||
return Some(strength);
|
||||
}
|
||||
|
||||
let agility = self.agility.raw().cmp(&other.agility.raw());
|
||||
|
||||
if agility == Ordering::Less {
|
||||
return Some(agility);
|
||||
}
|
||||
|
||||
let intelligence = self.intelligence.raw().cmp(&other.intelligence.raw());
|
||||
|
||||
if intelligence == Ordering::Less {
|
||||
return Some(intelligence);
|
||||
}
|
||||
|
||||
if strength == Ordering::Equal
|
||||
&& agility == Ordering::Equal
|
||||
&& intelligence == Ordering::Equal
|
||||
{
|
||||
return Some(Ordering::Equal);
|
||||
}
|
||||
|
||||
if strength == Ordering::Greater
|
||||
&& agility == Ordering::Greater
|
||||
&& intelligence == Ordering::Greater
|
||||
{
|
||||
return Some(Ordering::Greater);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for Attributes {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for Attributes {
|
||||
fn debug_name() -> &'static str {
|
||||
"Attributes"
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use super::statistics::*;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct CharacterStatus {
|
||||
pub current_health: Health,
|
||||
pub current_mana: Mana,
|
||||
|
||||
pub last_tick: Option<Duration>,
|
||||
}
|
||||
|
||||
impl CharacterStatus {
|
||||
pub fn new_full(stats: &Statistics) -> Self {
|
||||
CharacterStatus {
|
||||
current_health: stats.health,
|
||||
current_mana: stats.mana,
|
||||
|
||||
last_tick: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn regen_tick(&mut self, stats: &Statistics) {
|
||||
if !self.is_dead() {
|
||||
self.add_health(stats.health_regeneration.raw(), stats);
|
||||
self.add_mana(stats.mana_regeneration.raw(), stats);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_ability(&mut self, cost: f32) -> bool {
|
||||
self.use_mana(cost)
|
||||
}
|
||||
|
||||
pub fn add_health(&mut self, health: impl Into<Health>, stats: &Statistics) {
|
||||
let health_bonus = health.into();
|
||||
|
||||
self.current_health = if self.current_health + health_bonus > stats.health {
|
||||
stats.health
|
||||
} else {
|
||||
self.current_health + health_bonus
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_mana(&mut self, mana: impl Into<Mana>, stats: &Statistics) {
|
||||
let mana_bonus = mana.into();
|
||||
|
||||
self.current_mana = if self.current_mana + mana_bonus > stats.mana {
|
||||
stats.mana
|
||||
} else {
|
||||
self.current_mana + mana_bonus
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_damage(&mut self, cost: f32) -> bool {
|
||||
self.reduce_health(cost)
|
||||
}
|
||||
|
||||
// returns false if there isn't enough mana left, otherwise true
|
||||
fn use_mana(&mut self, mana: impl Into<Mana>) -> bool {
|
||||
let mana = mana.into();
|
||||
|
||||
if mana > self.current_mana {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.current_mana = self.current_mana - mana;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// returns false if there isn't enough health present
|
||||
fn reduce_health(&mut self, health: impl Into<Health>) -> bool {
|
||||
let health = health.into();
|
||||
|
||||
if health > self.current_health {
|
||||
self.current_health = Health::from(0.0);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.current_health = self.current_health - health;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_dead(&self) -> bool {
|
||||
self.current_health == Health::from(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for CharacterStatus {
|
||||
fn enable(&mut self, _world: &mut World) -> Result<()> {
|
||||
self.last_tick = None;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for CharacterStatus {
|
||||
fn debug_name() -> &'static str {
|
||||
"CharacterStatus"
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
use paste::paste;
|
||||
|
||||
use crate::{config::save_game::SaveGame, items::Rarities};
|
||||
|
||||
macro_rules! check_consume {
|
||||
($self: ident, $var: ident, $amount: ident) => {
|
||||
paste! {
|
||||
if $self.$var < $amount {
|
||||
false
|
||||
} else {
|
||||
$self.$var = $self.$var - $amount;
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct CraftingMaterials {
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
|
||||
impl CraftingMaterials {
|
||||
pub fn load(save_game: &SaveGame) -> Self {
|
||||
let mut me = Self::default();
|
||||
|
||||
me.common = save_game.crafting_materials.common;
|
||||
me.uncommon = save_game.crafting_materials.uncommon;
|
||||
me.magical = save_game.crafting_materials.magical;
|
||||
me.rare = save_game.crafting_materials.rare;
|
||||
me.epic = save_game.crafting_materials.epic;
|
||||
me.legendary = save_game.crafting_materials.legendary;
|
||||
|
||||
me
|
||||
}
|
||||
|
||||
pub fn store(&self, save_game: &mut SaveGame) {
|
||||
save_game.crafting_materials.common = self.common;
|
||||
save_game.crafting_materials.uncommon = self.uncommon;
|
||||
save_game.crafting_materials.magical = self.magical;
|
||||
save_game.crafting_materials.rare = self.rare;
|
||||
save_game.crafting_materials.epic = self.epic;
|
||||
save_game.crafting_materials.legendary = self.legendary;
|
||||
}
|
||||
|
||||
pub fn count(&self, rarity: Rarities) -> u32 {
|
||||
match rarity {
|
||||
Rarities::Common => self.common,
|
||||
Rarities::Uncommon => self.uncommon,
|
||||
Rarities::Magical => self.magical,
|
||||
Rarities::Rare => self.rare,
|
||||
Rarities::Epic => self.epic,
|
||||
Rarities::Legendary => self.legendary,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, rarity: Rarities) {
|
||||
match rarity {
|
||||
Rarities::Common => self.common += 1,
|
||||
Rarities::Uncommon => self.uncommon += 1,
|
||||
Rarities::Magical => self.magical += 1,
|
||||
Rarities::Rare => self.rare += 1,
|
||||
Rarities::Epic => self.epic += 1,
|
||||
Rarities::Legendary => self.legendary += 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, rarity: Rarities, amount: u32) -> bool {
|
||||
match rarity {
|
||||
Rarities::Common => check_consume!(self, common, amount),
|
||||
Rarities::Uncommon => check_consume!(self, uncommon, amount),
|
||||
Rarities::Magical => check_consume!(self, magical, amount),
|
||||
Rarities::Rare => check_consume!(self, rare, amount),
|
||||
Rarities::Epic => check_consume!(self, rare, amount),
|
||||
Rarities::Legendary => check_consume!(self, legendary, amount),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for CraftingMaterials {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for CraftingMaterials {
|
||||
fn debug_name() -> &'static str {
|
||||
"CraftingMaterials"
|
||||
}
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use std::slice::Iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
config::save_game::SaveGame,
|
||||
items::{
|
||||
ability_addon::AbilityAddon,
|
||||
ability_book::{Ability, AbilityBook},
|
||||
Item, ItemSystem, Jewel, MapItem, Rarities,
|
||||
},
|
||||
};
|
||||
|
||||
pub trait Storable {
|
||||
fn rarity(&self) -> Rarities;
|
||||
fn icon(&self) -> Arc<Image>;
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Inventory<A: Ability> {
|
||||
items: Vec<Item>,
|
||||
addons: Vec<AbilityAddon>,
|
||||
books: Vec<AbilityBook<A>>,
|
||||
jewels: Vec<Jewel>,
|
||||
maps: Vec<MapItem>,
|
||||
}
|
||||
|
||||
impl<A: Ability> Inventory<A> {
|
||||
// ------- items --------
|
||||
pub fn add_item(&mut self, item: Item) {
|
||||
self.items.push(item);
|
||||
}
|
||||
|
||||
pub fn insert_item(&mut self, item: Item, index: usize) {
|
||||
self.items.insert(index, item);
|
||||
}
|
||||
|
||||
pub fn remove_item(&mut self, index: usize) -> Item {
|
||||
self.items.remove(index)
|
||||
}
|
||||
|
||||
pub fn iter_items(&self) -> Iter<'_, Item> {
|
||||
self.items.iter()
|
||||
}
|
||||
|
||||
pub fn item_at(&self, index: usize) -> &Item {
|
||||
&self.items[index]
|
||||
}
|
||||
|
||||
pub fn item_mut_at(&mut self, index: usize) -> &mut Item {
|
||||
&mut self.items[index]
|
||||
}
|
||||
|
||||
// ------- jewels --------
|
||||
pub fn add_jewel(&mut self, jewel: Jewel) {
|
||||
self.jewels.push(jewel);
|
||||
}
|
||||
|
||||
pub fn insert_jewel(&mut self, jewel: Jewel, index: usize) {
|
||||
self.jewels.insert(index, jewel);
|
||||
}
|
||||
|
||||
pub fn remove_jewel(&mut self, index: usize) -> Jewel {
|
||||
self.jewels.remove(index)
|
||||
}
|
||||
|
||||
pub fn iter_jewels(&self) -> Iter<'_, Jewel> {
|
||||
self.jewels.iter()
|
||||
}
|
||||
|
||||
pub fn jewel_at(&self, index: usize) -> &Jewel {
|
||||
&self.jewels[index]
|
||||
}
|
||||
|
||||
pub fn jewel_mut_at(&mut self, index: usize) -> &mut Jewel {
|
||||
&mut self.jewels[index]
|
||||
}
|
||||
|
||||
// ------- maps --------
|
||||
pub fn add_map(&mut self, map: MapItem) {
|
||||
self.maps.push(map);
|
||||
}
|
||||
|
||||
pub fn insert_map(&mut self, map: MapItem, index: usize) {
|
||||
self.maps.insert(index, map);
|
||||
}
|
||||
|
||||
pub fn remove_map(&mut self, index: usize) -> MapItem {
|
||||
self.maps.remove(index)
|
||||
}
|
||||
|
||||
pub fn iter_maps(&self) -> Iter<'_, MapItem> {
|
||||
self.maps.iter()
|
||||
}
|
||||
|
||||
pub fn maps_at(&self, index: usize) -> &MapItem {
|
||||
&self.maps[index]
|
||||
}
|
||||
|
||||
// ------- addons --------
|
||||
pub fn add_addon(&mut self, addon: AbilityAddon) {
|
||||
self.addons.push(addon);
|
||||
}
|
||||
|
||||
// pub fn insert_addon(&mut self, addon: AbilityAddon, index: usize) {
|
||||
// self.addons.insert(index, addon);
|
||||
// }
|
||||
|
||||
pub fn remove_addon(&mut self, index: usize) -> AbilityAddon {
|
||||
self.addons.remove(index)
|
||||
}
|
||||
|
||||
pub fn addon(&self, index: usize) -> &AbilityAddon {
|
||||
&self.addons[index]
|
||||
}
|
||||
|
||||
pub fn iter_addons(&self) -> Iter<'_, AbilityAddon> {
|
||||
self.addons.iter()
|
||||
}
|
||||
|
||||
pub fn addon_at(&self, index: usize) -> &AbilityAddon {
|
||||
&self.addons[index]
|
||||
}
|
||||
|
||||
// ------- books --------
|
||||
pub fn add_book(&mut self, book: AbilityBook<A>) {
|
||||
self.books.push(book);
|
||||
}
|
||||
|
||||
pub fn insert_book(&mut self, book: AbilityBook<A>, index: usize) {
|
||||
self.books.insert(index, book);
|
||||
}
|
||||
|
||||
pub fn remove_book(&mut self, index: usize) -> AbilityBook<A> {
|
||||
self.books.remove(index)
|
||||
}
|
||||
|
||||
pub fn iter_books(&self) -> Iter<'_, AbilityBook<A>> {
|
||||
self.books.iter()
|
||||
}
|
||||
|
||||
pub fn book_at(&self, index: usize) -> &AbilityBook<A> {
|
||||
&self.books[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability> Inventory<A> {
|
||||
pub fn load(save_game: &SaveGame, item_system: &ItemSystem<A>) -> Result<Self> {
|
||||
let mut inventory: Inventory<A> = Default::default();
|
||||
|
||||
for item_string in save_game.inventory.items.iter() {
|
||||
if !item_string.is_empty() {
|
||||
inventory.add_item(Item::from_persistent(item_string.split('|'), item_system)?);
|
||||
}
|
||||
}
|
||||
|
||||
for addon_string in save_game.inventory.addons.iter() {
|
||||
if !addon_string.is_empty() {
|
||||
inventory.add_addon(AbilityAddon::from_persistent(
|
||||
addon_string.split('|'),
|
||||
item_system,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
for book_string in save_game.inventory.books.iter() {
|
||||
if !book_string.is_empty() {
|
||||
inventory.add_book(AbilityBook::from_persistent(
|
||||
book_string.split('|'),
|
||||
item_system,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
for jewel_string in save_game.inventory.jewels.iter() {
|
||||
if !jewel_string.is_empty() {
|
||||
let mut jewel = Jewel::from_persistent(&mut jewel_string.split('|'))?;
|
||||
|
||||
jewel.icon =
|
||||
Some(item_system.jewel_icon(jewel.rarity, jewel.level, jewel.attribute));
|
||||
jewel.update_stat(&item_system.item_settings);
|
||||
|
||||
inventory.add_jewel(jewel);
|
||||
}
|
||||
}
|
||||
|
||||
for map_string in save_game.inventory.maps.iter() {
|
||||
if !map_string.is_empty() {
|
||||
inventory.add_map(MapItem::from_persistent(
|
||||
map_string.split('|'),
|
||||
item_system,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(inventory)
|
||||
}
|
||||
|
||||
pub fn store(&self, save_game: &mut SaveGame) {
|
||||
for item in self.items.iter() {
|
||||
save_game.inventory.items.push(item.into_persistent());
|
||||
}
|
||||
|
||||
for addon in self.addons.iter() {
|
||||
save_game.inventory.addons.push(addon.into_persistent());
|
||||
}
|
||||
|
||||
for book in self.books.iter() {
|
||||
save_game.inventory.books.push(book.into_persistent());
|
||||
}
|
||||
|
||||
for jewel in self.jewels.iter() {
|
||||
save_game.inventory.jewels.push(jewel.into_persistent());
|
||||
}
|
||||
|
||||
for map in self.maps.iter() {
|
||||
save_game.inventory.maps.push(map.into_persistent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability + 'static> EntityComponent for Inventory<A> {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability> ComponentDebug for Inventory<A> {
|
||||
fn debug_name() -> &'static str {
|
||||
"Inventory"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,96 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use crate::config::experience::ExperienceSettings;
|
||||
|
||||
use super::npc_type::NPCType;
|
||||
|
||||
pub struct LevelUpEvent;
|
||||
|
||||
pub struct Level {
|
||||
current_level: u32,
|
||||
pub current_experience: u32,
|
||||
pub experience_needed: u32,
|
||||
|
||||
exp_settings: ExperienceSettings,
|
||||
}
|
||||
|
||||
impl Clone for Level {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
current_level: self.current_level,
|
||||
current_experience: self.current_experience,
|
||||
experience_needed: self.experience_needed,
|
||||
|
||||
exp_settings: self.exp_settings.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Level {
|
||||
pub fn new(exp_settings: &ExperienceSettings) -> Self {
|
||||
Level {
|
||||
current_level: 1,
|
||||
|
||||
current_experience: 0,
|
||||
experience_needed: exp_settings.player_experience_needed(1),
|
||||
|
||||
exp_settings: exp_settings.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(level: u32, current_experience: u32, exp_settings: &ExperienceSettings) -> Self {
|
||||
Level {
|
||||
current_level: level,
|
||||
|
||||
current_experience,
|
||||
experience_needed: exp_settings.player_experience_needed(level),
|
||||
|
||||
exp_settings: exp_settings.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns `true` at level up
|
||||
pub fn add_experience(&mut self, mob_level: u32, npc_type: impl Into<NPCType>) -> Result<bool> {
|
||||
self.add_experience_raw(self.exp_settings.mob_experience(mob_level, npc_type))
|
||||
}
|
||||
|
||||
pub fn add_experience_raw(&mut self, exp: u32) -> Result<bool> {
|
||||
self.current_experience += exp;
|
||||
|
||||
while self.current_experience >= self.experience_needed {
|
||||
self.current_level += 1;
|
||||
|
||||
self.current_experience -= self.experience_needed;
|
||||
self.experience_needed = self
|
||||
.exp_settings
|
||||
.player_experience_needed(self.current_level);
|
||||
|
||||
if self.current_experience < self.experience_needed {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub fn level(&self) -> u32 {
|
||||
self.current_level
|
||||
}
|
||||
|
||||
pub fn reset_current(&mut self) {
|
||||
self.current_experience = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for Level {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for Level {
|
||||
fn debug_name() -> &'static str {
|
||||
"Level"
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
macro_rules! generate_stat {
|
||||
($struct_name:ident, $data_type:ty, $display_name:expr) => {
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct $struct_name($data_type);
|
||||
|
||||
impl $struct_name {
|
||||
pub const DISPLAY_NAME: &'static str = $display_name;
|
||||
|
||||
pub fn set(&mut self, v: $data_type) {
|
||||
self.0 = v;
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> $data_type {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
$display_name
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for $struct_name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, $display_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$data_type> for $struct_name {
|
||||
fn from(value: $data_type) -> Self {
|
||||
$struct_name(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&$struct_name> for $struct_name {
|
||||
fn from(value: &$struct_name) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for $struct_name {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: $struct_name) -> Self {
|
||||
$struct_name(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for $struct_name {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: $struct_name) -> Self {
|
||||
$struct_name(self.0 - other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for $struct_name {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: $struct_name) -> Self {
|
||||
$struct_name(self.0 * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign for $struct_name {
|
||||
fn add_assign(&mut self, other: $struct_name) {
|
||||
self.0 += other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::SubAssign for $struct_name {
|
||||
fn sub_assign(&mut self, other: $struct_name) {
|
||||
self.0 -= other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::MulAssign for $struct_name {
|
||||
fn mul_assign(&mut self, other: $struct_name) {
|
||||
self.0 *= other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::MulAssign<f32> for $struct_name {
|
||||
fn mul_assign(&mut self, other: f32) {
|
||||
self.0 = (self.0 as f32 * other) as $data_type;
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for $struct_name {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $struct_name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for $struct_name {}
|
||||
};
|
||||
}
|
||||
|
||||
use super::attributes::Attributes;
|
||||
|
||||
pub trait AttributeAssociation {
|
||||
fn attribute_level(&self, attributes: &Attributes) -> u32;
|
||||
}
|
||||
|
||||
macro_rules! associate_agility {
|
||||
($struct_name:ident) => {
|
||||
impl AttributeAssociation for $struct_name {
|
||||
fn attribute_level(&self, attributes: &Attributes) -> u32 {
|
||||
attributes.agility().raw()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! associate_intelligence {
|
||||
($struct_name:ident) => {
|
||||
impl AttributeAssociation for $struct_name {
|
||||
fn attribute_level(&self, attributes: &Attributes) -> u32 {
|
||||
attributes.intelligence().raw()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! associate_strength {
|
||||
($struct_name:ident) => {
|
||||
impl AttributeAssociation for $struct_name {
|
||||
fn attribute_level(&self, attributes: &Attributes) -> u32 {
|
||||
attributes.strength().raw()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod ability_slots;
|
||||
pub mod attributes;
|
||||
pub mod character_status;
|
||||
pub mod crafting_materials;
|
||||
pub mod inventory;
|
||||
pub mod item_slots;
|
||||
pub mod level;
|
||||
pub mod npc_type;
|
||||
pub mod statistic_types;
|
||||
pub mod statistics;
|
|
@ -1,101 +0,0 @@
|
|||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use engine::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct NPCNormal;
|
||||
|
||||
impl EntityComponent for NPCNormal {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for NPCNormal {
|
||||
fn debug_name() -> &'static str {
|
||||
"NPCNormal"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct NPCElite;
|
||||
|
||||
impl EntityComponent for NPCElite {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for NPCElite {
|
||||
fn debug_name() -> &'static str {
|
||||
"NPCElite"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct NPCBoss;
|
||||
|
||||
impl EntityComponent for NPCBoss {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for NPCBoss {
|
||||
fn debug_name() -> &'static str {
|
||||
"NPCBoss"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum NPCType {
|
||||
#[default]
|
||||
Normal,
|
||||
Elite,
|
||||
Boss,
|
||||
}
|
||||
|
||||
impl From<NPCNormal> for NPCType {
|
||||
fn from(_: NPCNormal) -> Self {
|
||||
Self::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NPCElite> for NPCType {
|
||||
fn from(_: NPCElite) -> Self {
|
||||
Self::Elite
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NPCBoss> for NPCType {
|
||||
fn from(_: NPCBoss) -> Self {
|
||||
Self::Boss
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for NPCType {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"normal" => Ok(Self::Normal),
|
||||
"elite" => Ok(Self::Elite),
|
||||
"boss" => Ok(Self::Boss),
|
||||
|
||||
_ => Err(anyhow!("could not parse {s} as NPCType")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NPCType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
NPCType::Normal => write!(f, "normal"),
|
||||
NPCType::Elite => write!(f, "elite"),
|
||||
NPCType::Boss => write!(f, "boss"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,581 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::{attributes::Attributes, statistics::*};
|
||||
use crate::components::macros::AttributeAssociation;
|
||||
use crate::{config::items::ItemSettings, items::Rarities};
|
||||
|
||||
macro_rules! apply {
|
||||
(
|
||||
$self:ident, $attrib:ident,
|
||||
($level: ident, $rarity_multiplier:ident, $item_settings:ident),
|
||||
[$($elem:ident: $ty:ty $(,)? )+]
|
||||
) => {
|
||||
paste::paste! {
|
||||
match &mut $self {
|
||||
$(
|
||||
Self::$elem(v) => {
|
||||
v.set($level as $ty * $rarity_multiplier as $ty * $item_settings.$attrib.[< $elem:snake >]);
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum StrengthStatisticTypes {
|
||||
AirResistance(AirResistance),
|
||||
FireResistance(FireResistance),
|
||||
WaterResistance(WaterResistance),
|
||||
PhysicalDamage(PhysicalDamage),
|
||||
Health(Health),
|
||||
HealthRegeneration(HealthRegeneration),
|
||||
}
|
||||
|
||||
impl StrengthStatisticTypes {
|
||||
// total amount of types
|
||||
pub const TYPE_COUNT: u32 = 6;
|
||||
|
||||
pub fn random() -> Self {
|
||||
Random::range(0, Self::TYPE_COUNT).into()
|
||||
}
|
||||
|
||||
pub fn apply_for_jewel(
|
||||
mut self,
|
||||
level: u32,
|
||||
rarity: Rarities,
|
||||
item_settings: &ItemSettings,
|
||||
) -> Self {
|
||||
let rarity_multiplier = item_settings.jewel_rarity_multiplier.from_rarity(rarity);
|
||||
|
||||
apply!(
|
||||
self,
|
||||
per_strength_stats,
|
||||
(level, rarity_multiplier, item_settings),
|
||||
[
|
||||
AirResistance: u32,
|
||||
FireResistance: u32,
|
||||
WaterResistance: u32,
|
||||
PhysicalDamage: u32,
|
||||
Health: f32,
|
||||
HealthRegeneration: f32
|
||||
]
|
||||
);
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for StrengthStatisticTypes {
|
||||
fn from(n: u32) -> Self {
|
||||
match n {
|
||||
0 => Self::AirResistance(AirResistance::default()),
|
||||
1 => Self::FireResistance(FireResistance::default()),
|
||||
2 => Self::WaterResistance(WaterResistance::default()),
|
||||
3 => Self::PhysicalDamage(PhysicalDamage::default()),
|
||||
4 => Self::Health(Health::default()),
|
||||
5 => Self::HealthRegeneration(HealthRegeneration::default()),
|
||||
|
||||
_ => panic!("can only convert number below TYPE_COUNT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum AgilityStatisticTypes {
|
||||
Armor(Armor),
|
||||
CriticalHitChance(CriticalHitChance),
|
||||
CriticalHitDamage(CriticalHitDamage),
|
||||
}
|
||||
|
||||
impl AgilityStatisticTypes {
|
||||
// total amount of types
|
||||
pub const TYPE_COUNT: u32 = 3;
|
||||
|
||||
pub fn random() -> Self {
|
||||
Random::range(0, Self::TYPE_COUNT).into()
|
||||
}
|
||||
|
||||
pub fn apply_for_jewel(
|
||||
mut self,
|
||||
level: u32,
|
||||
rarity: Rarities,
|
||||
item_settings: &ItemSettings,
|
||||
) -> Self {
|
||||
let rarity_multiplier = item_settings.jewel_rarity_multiplier.from_rarity(rarity);
|
||||
|
||||
apply!(
|
||||
self,
|
||||
per_agility_stats,
|
||||
(level, rarity_multiplier, item_settings),
|
||||
[
|
||||
Armor: u32,
|
||||
CriticalHitChance: f32,
|
||||
CriticalHitDamage: f32
|
||||
]
|
||||
);
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for AgilityStatisticTypes {
|
||||
fn from(n: u32) -> Self {
|
||||
match n {
|
||||
0 => Self::Armor(Armor::default()),
|
||||
1 => Self::CriticalHitChance(CriticalHitChance::default()),
|
||||
2 => Self::CriticalHitDamage(CriticalHitDamage::default()),
|
||||
|
||||
_ => panic!("can only convert number below TYPE_COUNT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum IntelligenceStatisticTypes {
|
||||
AirDamage(AirDamage),
|
||||
FireDamage(FireDamage),
|
||||
WaterDamage(WaterDamage),
|
||||
Mana(Mana),
|
||||
ManaRegeneration(ManaRegeneration),
|
||||
}
|
||||
|
||||
impl IntelligenceStatisticTypes {
|
||||
// total amount of types
|
||||
pub const TYPE_COUNT: u32 = 5;
|
||||
|
||||
pub fn random() -> Self {
|
||||
Random::range(0, Self::TYPE_COUNT).into()
|
||||
}
|
||||
|
||||
pub fn apply_for_jewel(
|
||||
mut self,
|
||||
level: u32,
|
||||
rarity: Rarities,
|
||||
item_settings: &ItemSettings,
|
||||
) -> Self {
|
||||
let rarity_multiplier = item_settings.jewel_rarity_multiplier.from_rarity(rarity);
|
||||
|
||||
apply!(
|
||||
self,
|
||||
per_intelligence_stats,
|
||||
(level, rarity_multiplier, item_settings),
|
||||
[
|
||||
AirDamage: u32,
|
||||
FireDamage: u32,
|
||||
WaterDamage: u32,
|
||||
Mana: f32,
|
||||
ManaRegeneration: f32,
|
||||
]
|
||||
);
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for IntelligenceStatisticTypes {
|
||||
fn from(n: u32) -> Self {
|
||||
match n {
|
||||
0 => Self::AirDamage(AirDamage::default()),
|
||||
1 => Self::FireDamage(FireDamage::default()),
|
||||
2 => Self::WaterDamage(WaterDamage::default()),
|
||||
3 => Self::Mana(Mana::default()),
|
||||
4 => Self::ManaRegeneration(ManaRegeneration::default()),
|
||||
|
||||
_ => panic!("can only convert number below TYPE_COUNT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum StatisticType {
|
||||
AirResistance(AirResistance),
|
||||
AirDamage(AirDamage),
|
||||
|
||||
FireResistance(FireResistance),
|
||||
FireDamage(FireDamage),
|
||||
|
||||
WaterResistance(WaterResistance),
|
||||
WaterDamage(WaterDamage),
|
||||
|
||||
Armor(Armor),
|
||||
PhysicalDamage(PhysicalDamage),
|
||||
|
||||
CriticalHitChance(CriticalHitChance),
|
||||
CriticalHitDamage(CriticalHitDamage),
|
||||
|
||||
Health(Health),
|
||||
HealthRegeneration(HealthRegeneration),
|
||||
|
||||
Mana(Mana),
|
||||
ManaRegeneration(ManaRegeneration),
|
||||
}
|
||||
|
||||
impl StatisticType {
|
||||
// total amount of types
|
||||
pub const TYPE_COUNT: u32 = 14;
|
||||
|
||||
pub fn apply_for_item(&mut self, attributes: &Attributes, item_settings: &ItemSettings) {
|
||||
match self {
|
||||
// air
|
||||
StatisticType::AirResistance(air_resistance) => air_resistance.set(
|
||||
air_resistance.attribute_level(attributes)
|
||||
* item_settings.per_strength_stats.air_resistance,
|
||||
),
|
||||
|
||||
StatisticType::AirDamage(air_damage) => air_damage.set(
|
||||
air_damage.attribute_level(attributes)
|
||||
* item_settings.per_intelligence_stats.air_damage,
|
||||
),
|
||||
|
||||
// fire
|
||||
StatisticType::FireResistance(fire_resistance) => fire_resistance.set(
|
||||
fire_resistance.attribute_level(attributes)
|
||||
* item_settings.per_strength_stats.fire_resistance,
|
||||
),
|
||||
StatisticType::FireDamage(fire_damage) => fire_damage.set(
|
||||
fire_damage.attribute_level(attributes)
|
||||
* item_settings.per_intelligence_stats.fire_damage,
|
||||
),
|
||||
|
||||
// water
|
||||
StatisticType::WaterResistance(water_resistance) => water_resistance.set(
|
||||
water_resistance.attribute_level(attributes)
|
||||
* item_settings.per_strength_stats.water_resistance,
|
||||
),
|
||||
StatisticType::WaterDamage(water_damage) => water_damage.set(
|
||||
water_damage.attribute_level(attributes)
|
||||
* item_settings.per_intelligence_stats.water_damage,
|
||||
),
|
||||
|
||||
// physical
|
||||
StatisticType::Armor(armor) => {
|
||||
armor.set(armor.attribute_level(attributes) * item_settings.per_agility_stats.armor)
|
||||
}
|
||||
StatisticType::PhysicalDamage(physical_damage) => physical_damage.set(
|
||||
physical_damage.attribute_level(attributes)
|
||||
* item_settings.per_strength_stats.physical_damage,
|
||||
),
|
||||
|
||||
// crit
|
||||
StatisticType::CriticalHitChance(crit_chance) => crit_chance.set(
|
||||
crit_chance.attribute_level(attributes) as f32
|
||||
* item_settings.per_agility_stats.critical_hit_chance,
|
||||
),
|
||||
StatisticType::CriticalHitDamage(crit_damage) => crit_damage.set(
|
||||
crit_damage.attribute_level(attributes) as f32
|
||||
* item_settings.per_agility_stats.critical_hit_damage,
|
||||
),
|
||||
|
||||
// health
|
||||
StatisticType::Health(health) => health.set(
|
||||
health.attribute_level(attributes) as f32 * item_settings.per_strength_stats.health,
|
||||
),
|
||||
StatisticType::HealthRegeneration(health_regeneration) => health_regeneration.set(
|
||||
health_regeneration.attribute_level(attributes) as f32
|
||||
* item_settings.per_strength_stats.health_regeneration,
|
||||
),
|
||||
|
||||
// mana
|
||||
StatisticType::Mana(mana) => mana.set(
|
||||
mana.attribute_level(attributes) as f32 * item_settings.per_intelligence_stats.mana,
|
||||
),
|
||||
StatisticType::ManaRegeneration(mana_regeneration) => mana_regeneration.set(
|
||||
mana_regeneration.attribute_level(attributes) as f32
|
||||
* item_settings.per_intelligence_stats.mana_regeneration,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_for_jewel(&mut self, rarity: Rarities, level: u32, item_settings: &ItemSettings) {
|
||||
let factor = level * item_settings.jewel_rarity_multiplier.from_rarity(rarity);
|
||||
|
||||
match self {
|
||||
// air
|
||||
StatisticType::AirResistance(air_resistance) => {
|
||||
air_resistance.set(factor * item_settings.per_strength_stats.air_resistance)
|
||||
}
|
||||
|
||||
StatisticType::AirDamage(air_damage) => {
|
||||
air_damage.set(factor * item_settings.per_intelligence_stats.air_damage)
|
||||
}
|
||||
|
||||
// fire
|
||||
StatisticType::FireResistance(fire_resistance) => {
|
||||
fire_resistance.set(factor * item_settings.per_strength_stats.fire_resistance)
|
||||
}
|
||||
StatisticType::FireDamage(fire_damage) => {
|
||||
fire_damage.set(factor * item_settings.per_intelligence_stats.fire_damage)
|
||||
}
|
||||
|
||||
// water
|
||||
StatisticType::WaterResistance(water_resistance) => {
|
||||
water_resistance.set(factor * item_settings.per_strength_stats.water_resistance)
|
||||
}
|
||||
StatisticType::WaterDamage(water_damage) => {
|
||||
water_damage.set(factor * item_settings.per_intelligence_stats.water_damage)
|
||||
}
|
||||
|
||||
// physical
|
||||
StatisticType::Armor(armor) => {
|
||||
armor.set(factor * item_settings.per_agility_stats.armor)
|
||||
}
|
||||
StatisticType::PhysicalDamage(physical_damage) => {
|
||||
physical_damage.set(factor * item_settings.per_strength_stats.physical_damage)
|
||||
}
|
||||
|
||||
// crit
|
||||
StatisticType::CriticalHitChance(crit_chance) => {
|
||||
crit_chance.set(factor as f32 * item_settings.per_agility_stats.critical_hit_chance)
|
||||
}
|
||||
StatisticType::CriticalHitDamage(crit_damage) => {
|
||||
crit_damage.set(factor as f32 * item_settings.per_agility_stats.critical_hit_damage)
|
||||
}
|
||||
|
||||
// health
|
||||
StatisticType::Health(health) => {
|
||||
health.set(factor as f32 * item_settings.per_strength_stats.health)
|
||||
}
|
||||
StatisticType::HealthRegeneration(health_regeneration) => health_regeneration
|
||||
.set(factor as f32 * item_settings.per_strength_stats.health_regeneration),
|
||||
|
||||
// mana
|
||||
StatisticType::Mana(mana) => {
|
||||
mana.set(factor as f32 * item_settings.per_intelligence_stats.mana)
|
||||
}
|
||||
StatisticType::ManaRegeneration(mana_regeneration) => mana_regeneration
|
||||
.set(factor as f32 * item_settings.per_intelligence_stats.mana_regeneration),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn same_type(&self, other: &Self) -> bool {
|
||||
matches!(
|
||||
(self, other),
|
||||
(Self::AirResistance(_), Self::AirResistance(_))
|
||||
| (Self::AirDamage(_), Self::AirDamage(_))
|
||||
| (Self::FireResistance(_), Self::FireResistance(_))
|
||||
| (Self::FireDamage(_), Self::FireDamage(_))
|
||||
| (Self::WaterResistance(_), Self::WaterResistance(_))
|
||||
| (Self::WaterDamage(_), Self::WaterDamage(_))
|
||||
| (Self::Armor(_), Self::Armor(_))
|
||||
| (Self::PhysicalDamage(_), Self::PhysicalDamage(_))
|
||||
| (Self::CriticalHitChance(_), Self::CriticalHitChance(_))
|
||||
| (Self::CriticalHitDamage(_), Self::CriticalHitDamage(_))
|
||||
| (Self::Health(_), Self::Health(_))
|
||||
| (Self::HealthRegeneration(_), Self::HealthRegeneration(_))
|
||||
| (Self::Mana(_), Self::Mana(_))
|
||||
| (Self::ManaRegeneration(_), Self::ManaRegeneration(_))
|
||||
)
|
||||
}
|
||||
|
||||
pub fn display_value(&self) -> String {
|
||||
match *self {
|
||||
// air
|
||||
StatisticType::AirResistance(v) => format!("{}", v.raw()),
|
||||
StatisticType::AirDamage(v) => format!("{}", v.raw()),
|
||||
|
||||
// fire
|
||||
StatisticType::FireResistance(v) => format!("{}", v.raw()),
|
||||
StatisticType::FireDamage(v) => format!("{}", v.raw()),
|
||||
|
||||
// water
|
||||
StatisticType::WaterResistance(v) => format!("{}", v.raw()),
|
||||
StatisticType::WaterDamage(v) => format!("{}", v.raw()),
|
||||
|
||||
// physical
|
||||
StatisticType::Armor(v) => format!("{}", v.raw()),
|
||||
StatisticType::PhysicalDamage(v) => format!("{}", v.raw()),
|
||||
|
||||
// crit
|
||||
StatisticType::CriticalHitChance(v) => format!("{:.2} %", v.raw()),
|
||||
StatisticType::CriticalHitDamage(v) => format!("{:.2} %", v.raw()),
|
||||
|
||||
// health
|
||||
StatisticType::Health(v) => format!("{:.0}", v.raw()),
|
||||
StatisticType::HealthRegeneration(v) => format!("{:.2}", v.raw()),
|
||||
|
||||
// mana
|
||||
StatisticType::Mana(v) => format!("{:.0}", v.raw()),
|
||||
StatisticType::ManaRegeneration(v) => format!("{:.2}", v.raw()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign for StatisticType {
|
||||
fn add_assign(&mut self, other: StatisticType) {
|
||||
match (self, &other) {
|
||||
(Self::AirResistance(me), Self::AirResistance(val)) => *me += *val,
|
||||
(Self::AirDamage(me), Self::AirDamage(val)) => *me += *val,
|
||||
|
||||
(Self::FireResistance(me), Self::FireResistance(val)) => *me += *val,
|
||||
(Self::FireDamage(me), Self::FireDamage(val)) => *me += *val,
|
||||
|
||||
(Self::WaterResistance(me), Self::WaterResistance(val)) => *me += *val,
|
||||
(Self::WaterDamage(me), Self::WaterDamage(val)) => *me += *val,
|
||||
|
||||
(Self::Armor(me), Self::Armor(val)) => *me += *val,
|
||||
(Self::PhysicalDamage(me), Self::PhysicalDamage(val)) => *me += *val,
|
||||
|
||||
(Self::CriticalHitChance(me), Self::CriticalHitChance(val)) => *me += *val,
|
||||
(Self::CriticalHitDamage(me), Self::CriticalHitDamage(val)) => *me += *val,
|
||||
|
||||
(Self::Health(me), Self::Health(val)) => *me += *val,
|
||||
(Self::HealthRegeneration(me), Self::HealthRegeneration(val)) => *me += *val,
|
||||
|
||||
(Self::Mana(me), Self::Mana(val)) => *me += *val,
|
||||
(Self::ManaRegeneration(me), Self::ManaRegeneration(val)) => *me += *val,
|
||||
|
||||
_ => panic!("can not add different types"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for StatisticType {
|
||||
fn from(n: u32) -> Self {
|
||||
match n {
|
||||
// air
|
||||
0 => Self::AirResistance(AirResistance::default()),
|
||||
1 => Self::AirDamage(AirDamage::default()),
|
||||
|
||||
// fire
|
||||
2 => Self::FireResistance(FireResistance::default()),
|
||||
3 => Self::FireDamage(FireDamage::default()),
|
||||
|
||||
// water
|
||||
4 => Self::WaterResistance(WaterResistance::default()),
|
||||
5 => Self::WaterDamage(WaterDamage::default()),
|
||||
|
||||
// physical
|
||||
6 => Self::Armor(Armor::default()),
|
||||
7 => Self::PhysicalDamage(PhysicalDamage::default()),
|
||||
|
||||
// Crit
|
||||
8 => Self::CriticalHitChance(CriticalHitChance::default()),
|
||||
9 => Self::CriticalHitDamage(CriticalHitDamage::default()),
|
||||
|
||||
// health
|
||||
10 => Self::Health(Health::default()),
|
||||
11 => Self::HealthRegeneration(HealthRegeneration::default()),
|
||||
|
||||
// mana
|
||||
12 => Self::Mana(Mana::default()),
|
||||
13 => Self::ManaRegeneration(ManaRegeneration::default()),
|
||||
|
||||
_ => panic!("can only convert number below TYPE_COUNT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AgilityStatisticTypes> for StatisticType {
|
||||
fn from(agility_statistics: AgilityStatisticTypes) -> Self {
|
||||
match agility_statistics {
|
||||
AgilityStatisticTypes::CriticalHitChance(v) => StatisticType::CriticalHitChance(v),
|
||||
AgilityStatisticTypes::CriticalHitDamage(v) => StatisticType::CriticalHitDamage(v),
|
||||
AgilityStatisticTypes::Armor(v) => StatisticType::Armor(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IntelligenceStatisticTypes> for StatisticType {
|
||||
fn from(intelligence_statistics: IntelligenceStatisticTypes) -> Self {
|
||||
match intelligence_statistics {
|
||||
IntelligenceStatisticTypes::AirDamage(v) => StatisticType::AirDamage(v),
|
||||
IntelligenceStatisticTypes::FireDamage(v) => StatisticType::FireDamage(v),
|
||||
IntelligenceStatisticTypes::WaterDamage(v) => StatisticType::WaterDamage(v),
|
||||
IntelligenceStatisticTypes::Mana(v) => StatisticType::Mana(v),
|
||||
IntelligenceStatisticTypes::ManaRegeneration(v) => StatisticType::ManaRegeneration(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StrengthStatisticTypes> for StatisticType {
|
||||
fn from(strength_statistics: StrengthStatisticTypes) -> Self {
|
||||
match strength_statistics {
|
||||
StrengthStatisticTypes::AirResistance(v) => StatisticType::AirResistance(v),
|
||||
StrengthStatisticTypes::FireResistance(v) => StatisticType::FireResistance(v),
|
||||
StrengthStatisticTypes::WaterResistance(v) => StatisticType::WaterResistance(v),
|
||||
StrengthStatisticTypes::PhysicalDamage(v) => StatisticType::PhysicalDamage(v),
|
||||
StrengthStatisticTypes::Health(v) => StatisticType::Health(v),
|
||||
StrengthStatisticTypes::HealthRegeneration(v) => StatisticType::HealthRegeneration(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for StatisticType {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"Air Resistance" => Ok(Self::AirResistance(AirResistance::default())),
|
||||
"Air Damage" => Ok(Self::AirDamage(AirDamage::default())),
|
||||
|
||||
// fire
|
||||
"Fire Resistance" => Ok(Self::FireResistance(FireResistance::default())),
|
||||
"Fire Damage" => Ok(Self::FireDamage(FireDamage::default())),
|
||||
|
||||
// water
|
||||
"Water Resistance" => Ok(Self::WaterResistance(WaterResistance::default())),
|
||||
"Water Damage" => Ok(Self::WaterDamage(WaterDamage::default())),
|
||||
|
||||
// physical
|
||||
"Armor" => Ok(Self::Armor(Armor::default())),
|
||||
"Physical Damage" => Ok(Self::PhysicalDamage(PhysicalDamage::default())),
|
||||
|
||||
// Crit
|
||||
"Critical Hit Chance" => Ok(Self::CriticalHitChance(CriticalHitChance::default())),
|
||||
"Critical Hit Damage" => Ok(Self::CriticalHitDamage(CriticalHitDamage::default())),
|
||||
|
||||
// health
|
||||
"Health" => Ok(Self::Health(Health::default())),
|
||||
"Health Regeneration" => Ok(Self::HealthRegeneration(HealthRegeneration::default())),
|
||||
|
||||
// mana
|
||||
"Mana" => Ok(Self::Mana(Mana::default())),
|
||||
"Mana Regeneration" => Ok(Self::ManaRegeneration(ManaRegeneration::default())),
|
||||
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Failed parsing StatisticTypes from {}",
|
||||
s
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StatisticType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
// air
|
||||
StatisticType::AirResistance(_) => write!(f, "Air Resistance"),
|
||||
StatisticType::AirDamage(_) => write!(f, "Air Damage"),
|
||||
|
||||
// fire
|
||||
StatisticType::FireResistance(_) => write!(f, "Fire Resistance"),
|
||||
StatisticType::FireDamage(_) => write!(f, "Fire Damage"),
|
||||
|
||||
// water
|
||||
StatisticType::WaterResistance(_) => write!(f, "Water Resistance"),
|
||||
StatisticType::WaterDamage(_) => write!(f, "Water Damage"),
|
||||
|
||||
// physical
|
||||
StatisticType::Armor(_) => write!(f, "Armor"),
|
||||
StatisticType::PhysicalDamage(_) => write!(f, "Physical Damage"),
|
||||
|
||||
// crit
|
||||
StatisticType::CriticalHitChance(_) => write!(f, "Critical Hit Chance"),
|
||||
StatisticType::CriticalHitDamage(_) => write!(f, "Critical Hit Damage"),
|
||||
|
||||
// health
|
||||
StatisticType::Health(_) => write!(f, "Health"),
|
||||
StatisticType::HealthRegeneration(_) => write!(f, "Health Regeneration"),
|
||||
|
||||
// mana
|
||||
StatisticType::Mana(_) => write!(f, "Mana"),
|
||||
StatisticType::ManaRegeneration(_) => write!(f, "Mana Regeneration"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
use crate::{
|
||||
config::{
|
||||
attributes::{AgilitySettings, AttributeSettings, IntelligenceSettings, StrengthSettings},
|
||||
items::ItemSettings,
|
||||
},
|
||||
damage_type::DamageType,
|
||||
};
|
||||
|
||||
use super::{
|
||||
attributes::{Agility, Attributes, Intelligence, Strength},
|
||||
item_slots::ItemSlotContainer,
|
||||
macros::AttributeAssociation,
|
||||
statistic_types::StatisticType,
|
||||
};
|
||||
|
||||
// --------------- elements ---------------
|
||||
|
||||
// Air
|
||||
generate_stat!(AirResistance, u32, "Air Resistance");
|
||||
associate_strength!(AirResistance);
|
||||
|
||||
generate_stat!(AirDamage, u32, "Air Damage");
|
||||
associate_intelligence!(AirDamage);
|
||||
|
||||
// fire
|
||||
generate_stat!(FireResistance, u32, "Fire Resistance");
|
||||
associate_strength!(FireResistance);
|
||||
|
||||
generate_stat!(FireDamage, u32, "Fire Damage");
|
||||
associate_intelligence!(FireDamage);
|
||||
|
||||
// water
|
||||
generate_stat!(WaterResistance, u32, "Water Resistance");
|
||||
associate_strength!(WaterResistance);
|
||||
|
||||
generate_stat!(WaterDamage, u32, "Water Damage");
|
||||
associate_intelligence!(WaterDamage);
|
||||
|
||||
// physical
|
||||
generate_stat!(Armor, u32, "Armor");
|
||||
associate_agility!(Armor);
|
||||
|
||||
generate_stat!(PhysicalDamage, u32, "Physical Damage");
|
||||
associate_strength!(PhysicalDamage);
|
||||
|
||||
// ---------------- special ---------------
|
||||
|
||||
// crit
|
||||
generate_stat!(CriticalHitChance, f32, "Crit Chance");
|
||||
associate_agility!(CriticalHitChance);
|
||||
|
||||
generate_stat!(CriticalHitDamage, f32, "Crit Damage");
|
||||
associate_agility!(CriticalHitDamage);
|
||||
|
||||
// ----------------- base -----------------
|
||||
|
||||
// health
|
||||
generate_stat!(Health, f32, "Health");
|
||||
associate_strength!(Health);
|
||||
|
||||
impl std::ops::Add<HealthRegeneration> for Health {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: HealthRegeneration) -> Self {
|
||||
Health(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<HealthRegeneration> for Health {
|
||||
fn add_assign(&mut self, other: HealthRegeneration) {
|
||||
self.0 += other.0
|
||||
}
|
||||
}
|
||||
|
||||
generate_stat!(HealthRegeneration, f32, "Health Regeneration");
|
||||
associate_strength!(HealthRegeneration);
|
||||
|
||||
// mana
|
||||
generate_stat!(Mana, f32, "Mana");
|
||||
associate_intelligence!(Mana);
|
||||
|
||||
impl std::ops::Add<ManaRegeneration> for Mana {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: ManaRegeneration) -> Self {
|
||||
Mana(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<ManaRegeneration> for Mana {
|
||||
fn add_assign(&mut self, other: ManaRegeneration) {
|
||||
self.0 += other.0
|
||||
}
|
||||
}
|
||||
|
||||
generate_stat!(ManaRegeneration, f32, "Mana Regeneration");
|
||||
associate_intelligence!(ManaRegeneration);
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct Statistics {
|
||||
// air
|
||||
pub air_resistance: AirResistance,
|
||||
pub air_damage: AirDamage,
|
||||
|
||||
// fire
|
||||
pub fire_resistance: FireResistance,
|
||||
pub fire_damage: FireDamage,
|
||||
|
||||
// water
|
||||
pub water_resistance: WaterResistance,
|
||||
pub water_damage: WaterDamage,
|
||||
|
||||
// physical
|
||||
pub armor: Armor,
|
||||
pub physical_damage: PhysicalDamage,
|
||||
|
||||
// crit
|
||||
pub critical_hit_chance: CriticalHitChance,
|
||||
pub critical_hit_damage: CriticalHitDamage,
|
||||
|
||||
// health
|
||||
pub health: Health,
|
||||
pub health_regeneration: HealthRegeneration,
|
||||
|
||||
// mana
|
||||
pub mana: Mana,
|
||||
pub mana_regeneration: ManaRegeneration,
|
||||
}
|
||||
|
||||
impl Statistics {
|
||||
pub fn update<'a, 'b>(
|
||||
&mut self,
|
||||
attributes: &mut Attributes,
|
||||
attribute_settings: &AttributeSettings,
|
||||
|
||||
items: impl Into<Option<(&'a ItemSlotContainer, &'a ItemSettings)>>,
|
||||
) {
|
||||
*self = Self::default();
|
||||
let items = items.into();
|
||||
|
||||
if let Some((items, item_settings)) = items {
|
||||
let (agility, strength, intelligence) = items.collect_attribute_bonuses(item_settings);
|
||||
|
||||
attributes.bonus_agility = strength;
|
||||
attributes.bonus_strength = agility;
|
||||
attributes.bonus_intelligence = intelligence;
|
||||
}
|
||||
|
||||
// agility
|
||||
self.apply_agility(&attributes.agility(), &attribute_settings.agility_settings);
|
||||
|
||||
// strength
|
||||
self.apply_strength(
|
||||
&attributes.strength(),
|
||||
&attribute_settings.strength_settings,
|
||||
);
|
||||
|
||||
// intelligence
|
||||
self.apply_intelligence(
|
||||
&attributes.intelligence(),
|
||||
&attribute_settings.intelligence_settings,
|
||||
);
|
||||
|
||||
// apply items
|
||||
if let Some((items, _)) = items {
|
||||
let item_bonuses = items.collect_stat_bonuses();
|
||||
|
||||
for item_bonus in item_bonuses.into_iter() {
|
||||
self.apply_bonus(item_bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_resistance(&self, damage_type: DamageType) -> u32 {
|
||||
match damage_type {
|
||||
DamageType::Air => self.air_resistance.raw(),
|
||||
DamageType::Fire => self.fire_resistance.raw(),
|
||||
DamageType::Water => self.water_resistance.raw(),
|
||||
DamageType::Physical => self.armor.raw(),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_agility(&mut self, agility: &Agility, agility_settings: &AgilitySettings) {
|
||||
self.armor += Armor(agility.raw() * agility_settings.armor);
|
||||
self.critical_hit_chance +=
|
||||
CriticalHitChance(agility.raw() as f32 * agility_settings.crit_chance);
|
||||
self.critical_hit_damage +=
|
||||
CriticalHitDamage(agility.raw() as f32 * agility_settings.crit_damage);
|
||||
}
|
||||
|
||||
fn apply_strength(&mut self, strength: &Strength, strength_settings: &StrengthSettings) {
|
||||
self.health += Health(strength.raw() as f32 * strength_settings.health);
|
||||
self.health_regeneration +=
|
||||
HealthRegeneration(strength.raw() as f32 * strength_settings.health_regen);
|
||||
self.physical_damage += PhysicalDamage(strength.raw() * strength_settings.physical_damage);
|
||||
self.air_resistance += AirResistance(strength.raw() * strength_settings.air_resistance);
|
||||
self.fire_resistance += FireResistance(strength.raw() * strength_settings.fire_resistance);
|
||||
self.water_resistance +=
|
||||
WaterResistance(strength.raw() * strength_settings.water_resistance);
|
||||
}
|
||||
|
||||
fn apply_intelligence(
|
||||
&mut self,
|
||||
intelligence: &Intelligence,
|
||||
intelligence_settings: &IntelligenceSettings,
|
||||
) {
|
||||
self.mana += Mana(intelligence.raw() as f32 * intelligence_settings.mana);
|
||||
self.mana_regeneration +=
|
||||
ManaRegeneration(intelligence.raw() as f32 * intelligence_settings.mana_regen);
|
||||
self.air_damage += AirDamage(intelligence.raw() * intelligence_settings.air_damage);
|
||||
self.fire_damage += FireDamage(intelligence.raw() * intelligence_settings.fire_damage);
|
||||
self.water_damage += WaterDamage(intelligence.raw() * intelligence_settings.water_damage);
|
||||
}
|
||||
|
||||
fn apply_bonus(&mut self, stat_type: StatisticType) {
|
||||
match stat_type {
|
||||
StatisticType::AirResistance(air_resistance) => self.air_resistance += air_resistance,
|
||||
StatisticType::AirDamage(air_damage) => self.air_damage += air_damage,
|
||||
|
||||
StatisticType::FireResistance(fire_resistance) => {
|
||||
self.fire_resistance += fire_resistance
|
||||
}
|
||||
StatisticType::FireDamage(fire_damage) => self.fire_damage += fire_damage,
|
||||
|
||||
StatisticType::WaterResistance(water_resistance) => {
|
||||
self.water_resistance += water_resistance
|
||||
}
|
||||
StatisticType::WaterDamage(water_damage) => self.water_damage += water_damage,
|
||||
|
||||
StatisticType::Armor(armor) => self.armor += armor,
|
||||
StatisticType::PhysicalDamage(physical_damage) => {
|
||||
self.physical_damage += physical_damage
|
||||
}
|
||||
|
||||
StatisticType::CriticalHitChance(crit_chance) => {
|
||||
self.critical_hit_chance += crit_chance
|
||||
}
|
||||
StatisticType::CriticalHitDamage(crit_damage) => {
|
||||
self.critical_hit_damage += crit_damage
|
||||
}
|
||||
|
||||
StatisticType::Health(health) => self.health += health,
|
||||
StatisticType::HealthRegeneration(health_regen) => {
|
||||
self.health_regeneration += health_regen
|
||||
}
|
||||
|
||||
StatisticType::Mana(mana) => self.mana += mana,
|
||||
StatisticType::ManaRegeneration(mana_regen) => self.mana_regeneration += mana_regen,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for Statistics {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for Statistics {
|
||||
fn debug_name() -> &'static str {
|
||||
"Statistics"
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
|
||||
create_settings_section!(
|
||||
AbilityIcons,
|
||||
"Icons",
|
||||
{
|
||||
// ability book
|
||||
book: AssetPath,
|
||||
|
||||
// ability add ons
|
||||
damage: AssetPath,
|
||||
projectile_speed: AssetPath,
|
||||
bounce: AssetPath,
|
||||
explosion: AssetPath,
|
||||
size: AssetPath,
|
||||
additional_projectiles: AssetPath,
|
||||
cool_down: AssetPath,
|
||||
distance: AssetPath,
|
||||
|
||||
addon_background: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilityLevel,
|
||||
"AbilityLevel",
|
||||
{
|
||||
starting_cost: u32,
|
||||
cost_per_level: u32,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilitySlotCount,
|
||||
"RarityAddOnSlots",
|
||||
{
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(AbilitySlotCount, u32);
|
||||
|
||||
create_settings_section!(
|
||||
IntelligencePageRequirements,
|
||||
"IntelligencePerAbilityPage",
|
||||
{
|
||||
first: u32,
|
||||
second: u32,
|
||||
third: u32,
|
||||
fourth: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
ProjectileSpeedSettings,
|
||||
"ProjectileSpeed",
|
||||
{
|
||||
common: f32,
|
||||
uncommon: f32,
|
||||
magical: f32,
|
||||
rare: f32,
|
||||
epic: f32,
|
||||
legendary: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(ProjectileSpeedSettings, f32);
|
||||
|
||||
create_settings_section!(
|
||||
DamageSettings,
|
||||
"Damage",
|
||||
{
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(DamageSettings, u32);
|
||||
|
||||
create_settings_section!(
|
||||
ExplosionSettings,
|
||||
"Explosion",
|
||||
{
|
||||
common: f32,
|
||||
uncommon: f32,
|
||||
magical: f32,
|
||||
rare: f32,
|
||||
epic: f32,
|
||||
legendary: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(ExplosionSettings, f32);
|
||||
|
||||
create_settings_section!(
|
||||
SizeSettings,
|
||||
"Size",
|
||||
{
|
||||
common: f32,
|
||||
uncommon: f32,
|
||||
magical: f32,
|
||||
rare: f32,
|
||||
epic: f32,
|
||||
legendary: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(SizeSettings, f32);
|
||||
|
||||
create_settings_section!(
|
||||
AdditionalProjectilesSettings,
|
||||
"AdditionalProjectiles",
|
||||
{
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(AdditionalProjectilesSettings, u32);
|
||||
|
||||
create_settings_section!(
|
||||
CoolDownReductionSettings,
|
||||
"CoolDownReduction",
|
||||
{
|
||||
common: f32,
|
||||
uncommon: f32,
|
||||
magical: f32,
|
||||
rare: f32,
|
||||
epic: f32,
|
||||
legendary: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(CoolDownReductionSettings, f32);
|
||||
|
||||
create_settings_section!(
|
||||
DistanceSettings,
|
||||
"Distance",
|
||||
{
|
||||
common: f32,
|
||||
uncommon: f32,
|
||||
magical: f32,
|
||||
rare: f32,
|
||||
epic: f32,
|
||||
legendary: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(DistanceSettings, f32);
|
||||
|
||||
create_settings_container!(
|
||||
AbilitySettings,
|
||||
{
|
||||
icons: AbilityIcons,
|
||||
level: AbilityLevel,
|
||||
slot_count: AbilitySlotCount,
|
||||
page_requirements: IntelligencePageRequirements,
|
||||
|
||||
projectile_speed: ProjectileSpeedSettings,
|
||||
damage: DamageSettings,
|
||||
explosion: ExplosionSettings,
|
||||
size: SizeSettings,
|
||||
additional_projectiles: AdditionalProjectilesSettings,
|
||||
cool_down: CoolDownReductionSettings,
|
||||
distance: DistanceSettings,
|
||||
}
|
||||
);
|
|
@ -1,95 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
use crate::components::attributes::Attribute;
|
||||
|
||||
create_settings_section!(
|
||||
AgilitySettings,
|
||||
"Agility",
|
||||
{
|
||||
armor: u32,
|
||||
crit_chance: f32,
|
||||
crit_damage: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
StrengthSettings,
|
||||
"Strength",
|
||||
{
|
||||
physical_damage: u32,
|
||||
air_resistance: u32,
|
||||
fire_resistance: u32,
|
||||
water_resistance: u32,
|
||||
health: f32,
|
||||
health_regen: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
IntelligenceSettings,
|
||||
"Intelligence",
|
||||
{
|
||||
air_damage: u32,
|
||||
fire_damage: u32,
|
||||
water_damage: u32,
|
||||
mana: f32,
|
||||
mana_regen: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
MetaSection,
|
||||
"Meta",
|
||||
{
|
||||
starting_skill_points: u32,
|
||||
skill_points_per_level: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
StartingAttributes,
|
||||
"StartingAttributes",
|
||||
{
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AttributeColorSettings,
|
||||
"AttributeColorSettings",
|
||||
{
|
||||
strength: Color,
|
||||
agility: Color,
|
||||
intelligence: Color,
|
||||
}
|
||||
);
|
||||
|
||||
impl AttributeColorSettings {
|
||||
pub fn from_attribute(&self, attribute: Attribute) -> Color {
|
||||
match attribute {
|
||||
Attribute::Agility => self.agility,
|
||||
Attribute::Intelligence => self.intelligence,
|
||||
Attribute::Strength => self.strength,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StartingAttributes {
|
||||
pub fn sum(&self) -> u32 {
|
||||
self.strength + self.agility + self.intelligence
|
||||
}
|
||||
}
|
||||
|
||||
create_settings_container!(
|
||||
AttributeSettings,
|
||||
{
|
||||
meta_settings: MetaSection,
|
||||
agility_settings: AgilitySettings,
|
||||
strength_settings: StrengthSettings,
|
||||
intelligence_settings: IntelligenceSettings,
|
||||
starting_attributes: StartingAttributes,
|
||||
attribute_color_settings: AttributeColorSettings,
|
||||
}
|
||||
);
|
|
@ -1,65 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
use crate::components::npc_type::NPCType;
|
||||
|
||||
create_settings_section!(
|
||||
ExperienceCurveSettings,
|
||||
"Curve",
|
||||
{
|
||||
exponential: f32,
|
||||
square: f32,
|
||||
linear: f32,
|
||||
starting_point: f32,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
MobExperienceSettings,
|
||||
"Mobs",
|
||||
{
|
||||
linear: f32,
|
||||
starting_point: f32,
|
||||
elite_multiplier: f32,
|
||||
boss_multiplier: f32,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
ExperienceSettings,
|
||||
{
|
||||
mob_experience: MobExperienceSettings,
|
||||
experience_curve: ExperienceCurveSettings,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
impl ExperienceSettings {
|
||||
pub fn player_experience_needed(&self, level: u32) -> u32 {
|
||||
let exponential = self.experience_curve.exponential;
|
||||
let square = self.experience_curve.square;
|
||||
let linear = self.experience_curve.linear;
|
||||
let base = self.experience_curve.starting_point;
|
||||
let level = level as f32;
|
||||
|
||||
// x^exponential + square * x^2 + linear * x + base
|
||||
(level.powf(exponential) + square * level * level + linear * level + base) as u32
|
||||
}
|
||||
|
||||
pub fn mob_experience(&self, level: u32, npc_type: impl Into<NPCType>) -> u32 {
|
||||
let linear = self.mob_experience.linear;
|
||||
let base = self.mob_experience.starting_point;
|
||||
let level = level as f32;
|
||||
|
||||
let mut exp = linear * level + base;
|
||||
|
||||
match npc_type.into() {
|
||||
NPCType::Normal => (),
|
||||
NPCType::Elite => exp *= self.mob_experience.elite_multiplier,
|
||||
NPCType::Boss => exp *= self.mob_experience.boss_multiplier,
|
||||
}
|
||||
|
||||
exp as u32
|
||||
}
|
||||
}
|
|
@ -1,194 +0,0 @@
|
|||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
|
||||
create_settings_section!(
|
||||
GeneralItemSettings,
|
||||
"Meta",
|
||||
{
|
||||
attribute_multiplier: f32,
|
||||
icon_target_width: u32,
|
||||
icon_target_height: u32,
|
||||
drop_chance: f32,
|
||||
drop_chance_reference_level: u32,
|
||||
drop_chance_elite_multiplier: f32,
|
||||
drop_chance_boss_multiplier: f32,
|
||||
jewel_level_multiplier: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
RarityColorSettings,
|
||||
"RarityColors",
|
||||
{
|
||||
common: Color,
|
||||
uncommon: Color,
|
||||
magical: Color,
|
||||
rare: Color,
|
||||
epic: Color,
|
||||
legendary: Color,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(RarityColorSettings, Color);
|
||||
|
||||
create_settings_section!(
|
||||
RaritySlotSettings,
|
||||
"RarityStatisticSlots",
|
||||
{
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(RaritySlotSettings, u32);
|
||||
|
||||
create_settings_section!(
|
||||
RarityDropRates,
|
||||
"RarityDropRates",
|
||||
{
|
||||
common: f32,
|
||||
uncommon: f32,
|
||||
magical: f32,
|
||||
rare: f32,
|
||||
epic: f32,
|
||||
legendary: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(RarityDropRates, f32);
|
||||
|
||||
create_settings_section!(
|
||||
TypeDropRates,
|
||||
"TypeDropRates",
|
||||
{
|
||||
item: f32,
|
||||
ability_addon: f32,
|
||||
ability_book: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl RarityDropRates {
|
||||
pub fn verify(&self) {
|
||||
let sum =
|
||||
self.common + self.uncommon + self.magical + self.rare + self.epic + self.legendary;
|
||||
|
||||
assert!((sum - 1.0).abs() < 0.0001, "sum: {}", sum);
|
||||
}
|
||||
}
|
||||
|
||||
create_settings_section!(
|
||||
PerStrengthLevelStatistics,
|
||||
"PerStrengthStats",
|
||||
{
|
||||
health: f32,
|
||||
health_regeneration: f32,
|
||||
physical_damage: u32,
|
||||
air_resistance: u32,
|
||||
fire_resistance: u32,
|
||||
water_resistance: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
PerAgilityLevelStatistics,
|
||||
"PerAgilityStats",
|
||||
{
|
||||
armor: u32,
|
||||
critical_hit_chance: f32,
|
||||
critical_hit_damage: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
PerIntelligenceLevelStatistics,
|
||||
"PerIntelligenceStats",
|
||||
{
|
||||
air_damage: u32,
|
||||
fire_damage: u32,
|
||||
water_damage: u32,
|
||||
mana: f32,
|
||||
mana_regeneration: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
ItemIconPaths,
|
||||
"ItemIcons",
|
||||
{
|
||||
amulet: AssetPath,
|
||||
background: AssetPath,
|
||||
boots: AssetPath,
|
||||
chest: AssetPath,
|
||||
helmet: AssetPath,
|
||||
jewel: AssetPath,
|
||||
ring: AssetPath,
|
||||
belt: AssetPath,
|
||||
gloves: AssetPath,
|
||||
main_hand: AssetPath,
|
||||
off_hand: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
ItemPlaceHolderIconPaths,
|
||||
"ItemPlaceHolderIcons",
|
||||
{
|
||||
amulet: AssetPath,
|
||||
boots: AssetPath,
|
||||
chest: AssetPath,
|
||||
helmet: AssetPath,
|
||||
ring: AssetPath,
|
||||
belt: AssetPath,
|
||||
gloves: AssetPath,
|
||||
main_hand: AssetPath,
|
||||
off_hand: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
JewelIconPaths,
|
||||
"JewelIcons",
|
||||
{
|
||||
first: AssetPath,
|
||||
second: AssetPath,
|
||||
third: AssetPath,
|
||||
fourth: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
JewelRarityMultiplier,
|
||||
"JewelRarityMultiplier",
|
||||
{
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_from_rarity!(JewelRarityMultiplier, u32);
|
||||
|
||||
create_settings_container!(
|
||||
ItemSettings,
|
||||
{
|
||||
general: GeneralItemSettings,
|
||||
icon_paths: ItemIconPaths,
|
||||
icon_place_holder_paths: ItemPlaceHolderIconPaths,
|
||||
jewel_paths: JewelIconPaths,
|
||||
jewel_rarity_multiplier: JewelRarityMultiplier,
|
||||
rarity_color_settings: RarityColorSettings,
|
||||
rarity_slot_settings: RaritySlotSettings,
|
||||
rarity_drop_rates: RarityDropRates,
|
||||
type_drop_rates: TypeDropRates,
|
||||
per_strength_stats: PerStrengthLevelStatistics,
|
||||
per_agility_stats: PerAgilityLevelStatistics,
|
||||
per_intelligence_stats: PerIntelligenceLevelStatistics,
|
||||
}
|
||||
);
|
|
@ -1,25 +0,0 @@
|
|||
#[macro_use]
|
||||
pub mod create_section {
|
||||
macro_rules! impl_from_rarity {
|
||||
($struct_name:ident, $data_type:ty) => {
|
||||
impl $struct_name {
|
||||
pub fn from_rarity(&self, rarity: crate::items::Rarities) -> $data_type {
|
||||
match rarity {
|
||||
crate::items::Rarities::Common => self.common,
|
||||
crate::items::Rarities::Uncommon => self.uncommon,
|
||||
crate::items::Rarities::Magical => self.magical,
|
||||
crate::items::Rarities::Rare => self.rare,
|
||||
crate::items::Rarities::Epic => self.epic,
|
||||
crate::items::Rarities::Legendary => self.legendary,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub mod abilities;
|
||||
pub mod attributes;
|
||||
pub mod experience;
|
||||
pub mod items;
|
||||
pub mod save_game;
|
|
@ -1,402 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use crate::{
|
||||
components::{
|
||||
ability_slots::AbilitySlots, attributes::Attributes, character_status::CharacterStatus,
|
||||
crafting_materials::CraftingMaterials, inventory::Inventory, item_slots::ItemSlotContainer,
|
||||
level::Level, statistics::Statistics,
|
||||
},
|
||||
items::{ItemAffix, ItemSystem, Rarities, ability_book::Ability},
|
||||
};
|
||||
|
||||
use std::env::var;
|
||||
|
||||
use super::{attributes::AttributeSettings, experience::ExperienceSettings, items::ItemSettings};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn save_game_dir(game: &str) -> String {
|
||||
let b = var("LOCALAPPDATA").expect("couldn't get local appdata variable");
|
||||
|
||||
format!("{b}\\{game}\\saves\\")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn save_game_dir(game: &str) -> String {
|
||||
let b = var("HOME").expect("couldn't get HOME variable");
|
||||
|
||||
format!("{b}/.local/share/{game}/saves/")
|
||||
}
|
||||
|
||||
create_settings_section!(
|
||||
General,
|
||||
"General",
|
||||
{
|
||||
level: u32,
|
||||
exp: u32,
|
||||
name: String,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
HelmetSlot,
|
||||
"Helmet",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
ChestPlateSlot,
|
||||
"ChestPlate",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
BeltSlot,
|
||||
"Belt",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
GlovesSlot,
|
||||
"Gloves",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
BootsSlot,
|
||||
"Boots",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
FirstRingSlot,
|
||||
"Ring0",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
SecondRingSlot,
|
||||
"Ring1",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
ThirdRingSlot,
|
||||
"Ring2",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
FourthRingSlot,
|
||||
"Ring3",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
FirstAmuletSlot,
|
||||
"Amulet0",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
SecondAmuletSlot,
|
||||
"Amulet1",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
MainHandSlot,
|
||||
"MainHand",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
OffHandSlot,
|
||||
"OffHand",
|
||||
{
|
||||
used: bool,
|
||||
level: u32,
|
||||
strength: u32,
|
||||
agility: u32,
|
||||
intelligence: u32,
|
||||
rarity: Rarities,
|
||||
[affixes: ItemAffix],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
InventorySave,
|
||||
"Inventory",
|
||||
{
|
||||
[items: String],
|
||||
[addons: String],
|
||||
[books: String],
|
||||
[jewels: String],
|
||||
[maps: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
FirstAbilitySlot,
|
||||
"FirstAbilitySlot",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: u32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
SecondAbilitySlot,
|
||||
"SecondAbilitySlot",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: u32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
ThirdAbilitySlot,
|
||||
"ThirdAbilitySlot",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: u32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
FourthAbilitySlot,
|
||||
"FourthAbilitySlot",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: u32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
CraftingMaterialInfo,
|
||||
"CraftingMaterials",
|
||||
{
|
||||
common: u32,
|
||||
uncommon: u32,
|
||||
magical: u32,
|
||||
rare: u32,
|
||||
epic: u32,
|
||||
legendary: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
PassivesInfo,
|
||||
"Passives",
|
||||
{
|
||||
blood_mage: u32,
|
||||
vampire: u32,
|
||||
hermes: u32,
|
||||
thick_skinned: u32,
|
||||
super_brain: u32,
|
||||
survivalist: u32,
|
||||
soul_catcher: u32,
|
||||
pyromancer: u32,
|
||||
thunder_god: u32,
|
||||
sea_monster: u32,
|
||||
gladiator: u32,
|
||||
lucky_devil: u32,
|
||||
rogue: u32,
|
||||
alchemist: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
SaveGame,
|
||||
{
|
||||
general: General,
|
||||
|
||||
// items
|
||||
helmet: HelmetSlot,
|
||||
chest_plate: ChestPlateSlot,
|
||||
belt: BeltSlot,
|
||||
gloves: GlovesSlot,
|
||||
boots: BootsSlot,
|
||||
|
||||
// rings
|
||||
ring_0: FirstRingSlot,
|
||||
ring_1: SecondRingSlot,
|
||||
ring_2: ThirdRingSlot,
|
||||
ring_3: FourthRingSlot,
|
||||
|
||||
// amulets
|
||||
amulet_0: FirstAmuletSlot,
|
||||
amulet_1: SecondAmuletSlot,
|
||||
|
||||
// hands
|
||||
main_hand: MainHandSlot,
|
||||
off_hand: OffHandSlot,
|
||||
|
||||
// inventory
|
||||
inventory: InventorySave,
|
||||
|
||||
// abilities
|
||||
ability_0: FirstAbilitySlot,
|
||||
ability_1: SecondAbilitySlot,
|
||||
ability_2: ThirdAbilitySlot,
|
||||
ability_3: FourthAbilitySlot,
|
||||
|
||||
crafting_materials: CraftingMaterialInfo,
|
||||
|
||||
passives: PassivesInfo,
|
||||
}
|
||||
);
|
||||
|
||||
impl SaveGame {
|
||||
pub fn to_entity_object<A: Ability + 'static>(
|
||||
self,
|
||||
world: &mut World,
|
||||
) -> Result<(Entity, String)> {
|
||||
let mut entity_object = AssetHandler::create(world).empty_entity();
|
||||
|
||||
entity_object.insert_component(Draw::new(Vec::new()));
|
||||
entity_object.insert_component(Audio::new(world.resources.get_mut::<Context>(), None)?);
|
||||
Location::new_and_setup(&mut entity_object)?;
|
||||
|
||||
let experience_settings = world.resources.get::<ExperienceSettings>();
|
||||
let attribute_settings = world.resources.get::<AttributeSettings>();
|
||||
let item_settings = world.resources.get::<ItemSettings>();
|
||||
let item_system = world.resources.get::<ItemSystem<A>>();
|
||||
|
||||
let level = Level::load(self.general.level, self.general.exp, experience_settings);
|
||||
let mut attributes = Attributes::load(
|
||||
self.general.strength,
|
||||
self.general.agility,
|
||||
self.general.intelligence,
|
||||
);
|
||||
let inventory = Inventory::load(&self, &item_system)?;
|
||||
let abilities = AbilitySlots::load(item_system, &self)?;
|
||||
let crafting_materials = CraftingMaterials::load(&self);
|
||||
let items = ItemSlotContainer::load(&self, &item_system)?;
|
||||
let mut statistics = Statistics::default();
|
||||
statistics.update(&mut attributes, attribute_settings, (&items, item_settings));
|
||||
let current_status = CharacterStatus::new_full(&statistics);
|
||||
|
||||
entity_object.insert_component(level);
|
||||
entity_object.insert_component(attributes);
|
||||
entity_object.insert_component(inventory);
|
||||
entity_object.insert_component(abilities);
|
||||
entity_object.insert_component(crafting_materials);
|
||||
entity_object.insert_component(items);
|
||||
entity_object.insert_component(statistics);
|
||||
entity_object.insert_component(current_status);
|
||||
|
||||
Ok((world.add_entity(entity_object)?, self.general.name))
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DamageType {
|
||||
Physical,
|
||||
Fire,
|
||||
Water,
|
||||
Air,
|
||||
// TODO: More
|
||||
}
|
||||
|
||||
impl Default for DamageType {
|
||||
fn default() -> Self {
|
||||
Self::Physical
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for DamageType {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"Physical" => Ok(DamageType::Physical),
|
||||
"Fire" => Ok(DamageType::Fire),
|
||||
"Water" => Ok(DamageType::Water),
|
||||
"Air" => Ok(DamageType::Air),
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Failed parsing DamageType from {}",
|
||||
s
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DamageType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
DamageType::Physical => write!(f, "Physical"),
|
||||
DamageType::Fire => write!(f, "Fire"),
|
||||
DamageType::Water => write!(f, "Water"),
|
||||
DamageType::Air => write!(f, "Air"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Color> for DamageType {
|
||||
fn into(self) -> Color {
|
||||
// when changing colors, make sure you also change them in data/gui/xml/editor/abilityeditor/damage_type.xml
|
||||
|
||||
match self {
|
||||
DamageType::Physical => {
|
||||
Color::try_from("#A0522D").expect("could not convert sienna brown")
|
||||
}
|
||||
DamageType::Fire => Color::try_from("#F62817").expect("could not convert fire red"),
|
||||
DamageType::Water => Color::try_from("#105EE3").expect("could not convert blue"),
|
||||
DamageType::Air => Color::try_from("#20A6EB").expect("could not convert light blue"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,468 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
fmt::Debug,
|
||||
hash::{Hash, Hasher},
|
||||
slice::Iter,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{components::inventory::Storable, config::abilities::AbilitySettings};
|
||||
|
||||
use super::{ability_book::Ability, ItemSystem, Rarities, Tooltip};
|
||||
|
||||
const COOL_DOWN_REDUCTION_CAP: f32 = 0.7;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum AbilityAddonTypes {
|
||||
Damage(u32),
|
||||
ProjectileSpeed(f32),
|
||||
Bounce,
|
||||
Explosion(f32), // radius
|
||||
Size(f32),
|
||||
Projectiles(u32),
|
||||
CoolDown(f32), // %
|
||||
Distance(f32), // m
|
||||
// TODO: Pierce, // bool
|
||||
}
|
||||
|
||||
impl AbilityAddonTypes {
|
||||
pub const COUNT: u32 = 8;
|
||||
|
||||
pub fn iter() -> Iter<'static, Self> {
|
||||
use AbilityAddonTypes::*;
|
||||
|
||||
static ADD_ON_TYPES: [AbilityAddonTypes; AbilityAddonTypes::COUNT as usize] = [
|
||||
Damage(0),
|
||||
ProjectileSpeed(0.0),
|
||||
Bounce,
|
||||
Explosion(0.0),
|
||||
Size(0.0),
|
||||
Projectiles(0),
|
||||
CoolDown(0.0),
|
||||
Distance(0.0),
|
||||
];
|
||||
|
||||
ADD_ON_TYPES.iter()
|
||||
}
|
||||
|
||||
pub fn random() -> Self {
|
||||
let n = Random::range(0, Self::COUNT);
|
||||
|
||||
Self::from(n)
|
||||
}
|
||||
|
||||
pub fn val_as_str(&self) -> String {
|
||||
match self {
|
||||
Self::Damage(v) => format!("{} (*lvl)", v),
|
||||
Self::ProjectileSpeed(v) => format!("+ {:.1}", v),
|
||||
Self::Bounce => "Enabled".to_string(),
|
||||
Self::Explosion(v) => format!("+ {:.1}", v),
|
||||
Self::Size(v) => format!("+ {:.0}%", v * 100.0),
|
||||
Self::Projectiles(v) => format!("+ {}", v),
|
||||
Self::CoolDown(v) => format!("- {:.0}%", v * 100.0),
|
||||
Self::Distance(v) => format!("+ {}", v),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_rarity(&mut self, rarity: Rarities, ability_settings: &AbilitySettings) {
|
||||
match self {
|
||||
Self::Damage(v) => *v = ability_settings.damage.from_rarity(rarity),
|
||||
Self::ProjectileSpeed(v) => *v = ability_settings.projectile_speed.from_rarity(rarity),
|
||||
Self::Bounce => (),
|
||||
Self::Explosion(v) => *v = ability_settings.explosion.from_rarity(rarity),
|
||||
Self::Size(v) => *v = ability_settings.size.from_rarity(rarity),
|
||||
Self::Projectiles(v) => {
|
||||
*v = ability_settings.additional_projectiles.from_rarity(rarity)
|
||||
}
|
||||
Self::CoolDown(v) => {
|
||||
*v = ability_settings.cool_down.from_rarity(rarity);
|
||||
}
|
||||
Self::Distance(v) => *v = ability_settings.distance.from_rarity(rarity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_path<'a>(&self, ability_settings: &'a AbilitySettings) -> &'a AssetPath {
|
||||
match self {
|
||||
Self::Damage(_) => &ability_settings.icons.damage,
|
||||
Self::ProjectileSpeed(_) => &ability_settings.icons.projectile_speed,
|
||||
Self::Bounce => &ability_settings.icons.bounce,
|
||||
Self::Explosion(_) => &ability_settings.icons.explosion,
|
||||
Self::Size(_) => &ability_settings.icons.size,
|
||||
Self::Projectiles(_) => &ability_settings.icons.additional_projectiles,
|
||||
Self::CoolDown(_) => &ability_settings.icons.cool_down,
|
||||
Self::Distance(_) => &ability_settings.icons.distance,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_zero(self) -> Self {
|
||||
match self {
|
||||
AbilityAddonTypes::Damage(_) => AbilityAddonTypes::Damage(0),
|
||||
AbilityAddonTypes::ProjectileSpeed(_) => AbilityAddonTypes::ProjectileSpeed(0.0),
|
||||
AbilityAddonTypes::Bounce => AbilityAddonTypes::Bounce,
|
||||
AbilityAddonTypes::Explosion(_) => AbilityAddonTypes::Explosion(0.0),
|
||||
AbilityAddonTypes::Size(_) => AbilityAddonTypes::Size(0.0),
|
||||
AbilityAddonTypes::Projectiles(_) => AbilityAddonTypes::Projectiles(0),
|
||||
AbilityAddonTypes::CoolDown(_) => AbilityAddonTypes::CoolDown(0.0),
|
||||
AbilityAddonTypes::Distance(_) => AbilityAddonTypes::Distance(0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for AbilityAddonTypes {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"Damage" => Ok(Self::Damage(0)),
|
||||
"Projectile Speed" => Ok(Self::ProjectileSpeed(0.0)),
|
||||
"Bounce" => Ok(Self::Bounce),
|
||||
"Explosion" => Ok(Self::Explosion(0.0)),
|
||||
"Size" => Ok(Self::Size(0.0)),
|
||||
"Projectiles" => Ok(Self::Projectiles(0)),
|
||||
"Cool Down" => Ok(Self::CoolDown(0.0)),
|
||||
"Distance" => Ok(Self::Distance(0.0)),
|
||||
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Failed parsing AbilityAddonTypes from {}",
|
||||
s
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AbilityAddonTypes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Damage(_) => write!(f, "Damage"),
|
||||
Self::ProjectileSpeed(_) => write!(f, "Projectile Speed"),
|
||||
Self::Bounce => write!(f, "Bounce"),
|
||||
Self::Explosion(_) => write!(f, "Explosion"),
|
||||
Self::Size(_) => write!(f, "Size"),
|
||||
Self::Projectiles(_) => write!(f, "Projectiles"),
|
||||
Self::CoolDown(_) => write!(f, "Cool Down"),
|
||||
Self::Distance(_) => write!(f, "Distance"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for AbilityAddonTypes {
|
||||
fn from(n: u32) -> Self {
|
||||
match n {
|
||||
0 => Self::Damage(0),
|
||||
1 => Self::ProjectileSpeed(0.0),
|
||||
2 => Self::Bounce,
|
||||
3 => Self::Explosion(0.0),
|
||||
4 => Self::Size(0.0),
|
||||
5 => Self::Projectiles(0),
|
||||
6 => Self::CoolDown(0.0),
|
||||
7 => Self::Distance(0.0),
|
||||
|
||||
_ => panic!(
|
||||
"can't convert AbilityAddonTypes from bigger than {}",
|
||||
Self::COUNT
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl Hash for AbilityAddonTypes {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Damage(_) => 0u32.hash(state),
|
||||
Self::ProjectileSpeed(_) => 1u32.hash(state),
|
||||
Self::Bounce => 2u32.hash(state),
|
||||
Self::Explosion(_) => 3u32.hash(state),
|
||||
Self::Size(_) => 4u32.hash(state),
|
||||
Self::Projectiles(_) => 5u32.hash(state),
|
||||
Self::CoolDown(_) => 6u32.hash(state),
|
||||
Self::Distance(_) => 7u32.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for AbilityAddonTypes {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AbilityAddon {
|
||||
icon: Arc<Image>,
|
||||
|
||||
addon_type: AbilityAddonTypes,
|
||||
|
||||
rarity: Rarities,
|
||||
}
|
||||
|
||||
impl AbilityAddon {
|
||||
pub fn new(rarity: Rarities, addon_type: AbilityAddonTypes, icon: Arc<Image>) -> Self {
|
||||
AbilityAddon {
|
||||
addon_type,
|
||||
rarity,
|
||||
|
||||
icon,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
mut addon_type: AbilityAddonTypes,
|
||||
rarity: Rarities,
|
||||
icon: Arc<Image>,
|
||||
ability_settings: &AbilitySettings,
|
||||
) -> Self {
|
||||
addon_type.apply_rarity(rarity, ability_settings);
|
||||
|
||||
AbilityAddon {
|
||||
addon_type,
|
||||
rarity,
|
||||
|
||||
icon,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addon_type(&self) -> &AbilityAddonTypes {
|
||||
&self.addon_type
|
||||
}
|
||||
|
||||
pub fn into_persistent(&self) -> String {
|
||||
format!("{}|{}", self.addon_type(), self.rarity())
|
||||
}
|
||||
|
||||
pub fn from_persistent<'a, A: Ability>(
|
||||
mut split: impl Iterator<Item = &'a str>,
|
||||
item_system: &ItemSystem<A>,
|
||||
) -> Result<Self> {
|
||||
let addon_type = AbilityAddonTypes::from_str(split.next().unwrap())?;
|
||||
let rarity = Rarities::from_str(split.next().unwrap())?;
|
||||
|
||||
Ok(item_system.addon(rarity, addon_type))
|
||||
}
|
||||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let gui = GuiBuilder::from_str(
|
||||
gui_handler,
|
||||
include_str!("../../resources/addon_snippet.xml"),
|
||||
)?;
|
||||
|
||||
let icon: Arc<Icon> = gui.element("addon_icon")?;
|
||||
let rarity_label: Arc<Label> = gui.element("rarity_label")?;
|
||||
let type_label: Arc<Label> = gui.element("type")?;
|
||||
let value_label: Arc<Label> = gui.element("value")?;
|
||||
let grid: Arc<Grid> = gui.element("addon_grid")?;
|
||||
|
||||
grid.change_position_unscaled(position.0, position.1)?;
|
||||
|
||||
icon.set_icon(&self.icon())?;
|
||||
rarity_label.set_text(&format!("{}", self.rarity()))?;
|
||||
|
||||
type_label.set_text(&format!("{}", self.addon_type()))?;
|
||||
value_label.set_text(&self.addon_type().val_as_str())?;
|
||||
|
||||
Ok(Tooltip::new(grid, gui, gui_handler.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for AbilityAddon {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.addon_type == other.addon_type && self.rarity == other.rarity
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for AbilityAddon {
|
||||
fn rarity(&self) -> Rarities {
|
||||
self.rarity
|
||||
}
|
||||
|
||||
fn icon(&self) -> Arc<Image> {
|
||||
self.icon.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct AbilityAddonCollection {
|
||||
attached_addons: usize,
|
||||
addons: Vec<Option<AbilityAddon>>,
|
||||
}
|
||||
|
||||
impl AbilityAddonCollection {
|
||||
pub fn new(rarity: Rarities, ability_settings: &AbilitySettings) -> Self {
|
||||
let addons = (0..ability_settings.slot_count.from_rarity(rarity))
|
||||
.map(|_| None)
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
attached_addons: 0,
|
||||
addons,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
mut addons: Vec<Option<AbilityAddon>>,
|
||||
rarity: Rarities,
|
||||
ability_settings: &AbilitySettings,
|
||||
) -> Self {
|
||||
let attached = addons.len();
|
||||
let max = ability_settings.slot_count.from_rarity(rarity) as usize;
|
||||
|
||||
assert!(attached <= max);
|
||||
|
||||
let difference = max - attached;
|
||||
|
||||
// fill not set addons with None
|
||||
for _ in 0..difference {
|
||||
addons.push(None);
|
||||
}
|
||||
|
||||
Self {
|
||||
attached_addons: attached,
|
||||
addons,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_, Option<AbilityAddon>> {
|
||||
self.addons.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.addons.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.addons.is_empty()
|
||||
}
|
||||
|
||||
pub fn has_free_addon_slots(&self) -> bool {
|
||||
self.attached_addons < self.addons.len()
|
||||
}
|
||||
|
||||
pub fn attached_count(&self) -> usize {
|
||||
self.attached_addons
|
||||
}
|
||||
|
||||
pub fn insert_addon(&mut self, addon: AbilityAddon) {
|
||||
if !self.has_free_addon_slots() {
|
||||
panic!("Ability Book does not have any free slots left");
|
||||
}
|
||||
|
||||
self.addons[self.attached_addons] = Some(addon);
|
||||
self.attached_addons += 1;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(&AbilityAddonTypes),
|
||||
{
|
||||
for addon in self.addons.iter().flatten() {
|
||||
f(addon.addon_type());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn damage(&self) -> u32 {
|
||||
let mut damage = 0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::Damage(dmg) = addon_type {
|
||||
damage += dmg;
|
||||
}
|
||||
});
|
||||
|
||||
damage
|
||||
}
|
||||
|
||||
pub fn projectile_speed(&self) -> f32 {
|
||||
let mut speed = 0.0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::ProjectileSpeed(s) = addon_type {
|
||||
speed += s;
|
||||
}
|
||||
});
|
||||
|
||||
speed
|
||||
}
|
||||
|
||||
pub fn bounce(&self) -> bool {
|
||||
let mut bounce = false;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::Bounce = addon_type {
|
||||
bounce = true;
|
||||
}
|
||||
});
|
||||
|
||||
bounce
|
||||
}
|
||||
|
||||
pub fn explosion_radius(&self) -> f32 {
|
||||
let mut radius = 0.0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::Explosion(r) = addon_type {
|
||||
radius += r
|
||||
}
|
||||
});
|
||||
|
||||
radius
|
||||
}
|
||||
|
||||
pub fn size(&self) -> f32 {
|
||||
let mut size = 1.0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::Size(s) = addon_type {
|
||||
size += s;
|
||||
}
|
||||
});
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
pub fn additional_projectiles(&self) -> u32 {
|
||||
let mut projectiles = 0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::Projectiles(p) = addon_type {
|
||||
projectiles += p;
|
||||
}
|
||||
});
|
||||
|
||||
projectiles
|
||||
}
|
||||
|
||||
pub fn distance(&self) -> f32 {
|
||||
let mut distance = 0.0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::Distance(d) = addon_type {
|
||||
distance += d;
|
||||
}
|
||||
});
|
||||
|
||||
distance
|
||||
}
|
||||
|
||||
pub fn cool_down_reduction(&self) -> f32 {
|
||||
let mut cdr = 0.0;
|
||||
|
||||
self.map(|addon_type| {
|
||||
if let AbilityAddonTypes::CoolDown(cd) = addon_type {
|
||||
cdr += cd;
|
||||
}
|
||||
});
|
||||
|
||||
let cap = COOL_DOWN_REDUCTION_CAP;
|
||||
|
||||
cdr.min(cap)
|
||||
}
|
||||
}
|
|
@ -1,373 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use cgmath::{Vector2, Vector3};
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::{fmt::Debug, str::FromStr, sync::Arc, time::Duration};
|
||||
|
||||
use crate::{
|
||||
components::{
|
||||
character_status::CharacterStatus, crafting_materials::CraftingMaterials,
|
||||
inventory::Storable, statistics::Statistics,
|
||||
},
|
||||
config::abilities::{AbilityLevel, AbilitySettings},
|
||||
damage_type::DamageType,
|
||||
};
|
||||
|
||||
use super::{
|
||||
ItemSystem, Rarities, Tooltip,
|
||||
ability_addon::{AbilityAddon, AbilityAddonCollection, AbilityAddonTypes},
|
||||
};
|
||||
|
||||
pub trait Ability: Send + Sync + Clone + Default {
|
||||
fn create(context: &Context, asset_path: impl Into<AssetPath>) -> Result<Self>;
|
||||
|
||||
fn name(&self) -> &str;
|
||||
fn icon_path(&self) -> &AssetPath;
|
||||
|
||||
fn cool_down(&self) -> Duration;
|
||||
fn mana_cost(&self) -> u32;
|
||||
fn mana_cost_per_level(&self) -> u32;
|
||||
fn damage_type(&self) -> DamageType;
|
||||
fn base_damage(&self) -> u32;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct CastInformation {
|
||||
time: PersistentDuration,
|
||||
location: Vector3<f32>,
|
||||
direction: Vector2<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AbilityBook<A: Ability> {
|
||||
ability: A,
|
||||
|
||||
// meta
|
||||
icon: Arc<Image>,
|
||||
rarity: Rarities,
|
||||
|
||||
// addons
|
||||
addons: AbilityAddonCollection,
|
||||
|
||||
level: u32,
|
||||
ability_level_settings: AbilityLevel,
|
||||
|
||||
// cool down
|
||||
last_cast: Option<CastInformation>,
|
||||
}
|
||||
|
||||
impl<A: Ability> AbilityBook<A> {
|
||||
pub fn new(
|
||||
ability: A,
|
||||
icon: Arc<Image>,
|
||||
rarity: Rarities,
|
||||
ability_settings: &AbilitySettings,
|
||||
) -> Self {
|
||||
AbilityBook {
|
||||
ability,
|
||||
|
||||
// meta
|
||||
icon,
|
||||
rarity,
|
||||
|
||||
// addons
|
||||
addons: AbilityAddonCollection::new(rarity, ability_settings),
|
||||
|
||||
level: 1,
|
||||
ability_level_settings: ability_settings.level.clone(),
|
||||
|
||||
// cool down
|
||||
last_cast: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
ability: A,
|
||||
icon: Arc<Image>,
|
||||
rarity: Rarities,
|
||||
addons: Vec<Option<AbilityAddon>>,
|
||||
ability_settings: &AbilitySettings,
|
||||
level: u32,
|
||||
) -> Self {
|
||||
AbilityBook {
|
||||
ability,
|
||||
|
||||
icon,
|
||||
rarity,
|
||||
|
||||
addons: AbilityAddonCollection::load(addons, rarity, ability_settings),
|
||||
|
||||
level,
|
||||
ability_level_settings: ability_settings.level.clone(),
|
||||
|
||||
last_cast: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_mana(&self, character_status: &mut CharacterStatus) -> Result<bool> {
|
||||
let mana_costs = self.mana_cost();
|
||||
|
||||
Ok(character_status.use_ability(mana_costs as f32))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn validate_use(
|
||||
&mut self,
|
||||
now: Duration,
|
||||
character_status: &mut CharacterStatus,
|
||||
location: &Location,
|
||||
) -> Result<bool> {
|
||||
// don't allow anything while being animation locked
|
||||
|
||||
if let Some(cast_information) = &self.last_cast {
|
||||
let total_cool_down = Duration::from_secs_f32({
|
||||
let d = self.ability.cool_down();
|
||||
|
||||
d.as_secs_f32() * (1.0 - self.addons.cool_down_reduction())
|
||||
});
|
||||
|
||||
if (now - cast_information.time.into()) <= total_cool_down {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.check_mana(character_status)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// {
|
||||
// // TODO: further separation of animation types (bows, ...)
|
||||
// let animation_type = match self.ability.data().settings.parameter.damage_type {
|
||||
// DamageType::Physical => AnimationType::Attack,
|
||||
// _ => AnimationType::Cast,
|
||||
// };
|
||||
|
||||
// animation_info.set_animation(
|
||||
// animation,
|
||||
// draw,
|
||||
// Some(animation_type),
|
||||
// now,
|
||||
// true,
|
||||
// false,
|
||||
// )?;
|
||||
// }
|
||||
|
||||
self.set_cast(now, location);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn set_cast(&mut self, now: Duration, location: &Location) {
|
||||
self.last_cast = Some(CastInformation {
|
||||
time: now.into(),
|
||||
location: location.position(),
|
||||
direction: location.direction(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn last_cast(&self) -> &Option<CastInformation> {
|
||||
&self.last_cast
|
||||
}
|
||||
|
||||
pub fn check_cool_down(&mut self, now: Duration) -> Option<f32> {
|
||||
match &self.last_cast {
|
||||
Some(cast_information) => {
|
||||
let total_cool_down = Duration::from_secs_f32(
|
||||
self.ability.cool_down().as_secs_f32()
|
||||
* (1.0 - self.addons.cool_down_reduction()),
|
||||
);
|
||||
|
||||
let diff = now - cast_information.time.into();
|
||||
|
||||
if diff <= total_cool_down {
|
||||
Some((total_cool_down - diff).as_secs_f32())
|
||||
} else {
|
||||
self.last_cast = None;
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ability(&self) -> &A {
|
||||
&self.ability
|
||||
}
|
||||
|
||||
pub fn ability_mut(&mut self) -> &mut A {
|
||||
&mut self.ability
|
||||
}
|
||||
|
||||
pub fn addons(&self) -> &AbilityAddonCollection {
|
||||
&self.addons
|
||||
}
|
||||
|
||||
pub fn addons_mut(&mut self) -> &mut AbilityAddonCollection {
|
||||
&mut self.addons
|
||||
}
|
||||
|
||||
pub fn has_free_addon_slots(&self) -> bool {
|
||||
self.addons.has_free_addon_slots()
|
||||
}
|
||||
|
||||
pub fn attached_count(&self) -> usize {
|
||||
self.addons.attached_count()
|
||||
}
|
||||
|
||||
pub fn level(&self) -> u32 {
|
||||
self.level
|
||||
}
|
||||
|
||||
pub fn upgrade_cost(&self) -> u32 {
|
||||
self.ability_level_settings.starting_cost
|
||||
+ (self.level - 1) * self.ability_level_settings.cost_per_level
|
||||
}
|
||||
|
||||
pub fn upgrade(&mut self, crafting_materials: &mut CraftingMaterials) -> bool {
|
||||
if crafting_materials.consume(self.rarity, self.upgrade_cost()) {
|
||||
self.level += 1;
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// only used at a copy for ability upgrade tool tip
|
||||
pub fn dummy_uprade(&mut self) {
|
||||
self.level += 1;
|
||||
}
|
||||
|
||||
pub fn mana_cost(&self) -> u32 {
|
||||
self.ability.mana_cost() + self.ability.mana_cost_per_level() * (self.level - 1)
|
||||
}
|
||||
|
||||
pub fn damage(&self, statistics: &Statistics) -> u32 {
|
||||
// calculate damage of base ability
|
||||
let ability_base_damage = self.ability.base_damage();
|
||||
|
||||
// get bonus damage from statistics
|
||||
let stats_damage = match self.ability.damage_type() {
|
||||
DamageType::Air => statistics.air_damage.raw(),
|
||||
DamageType::Fire => statistics.fire_damage.raw(),
|
||||
DamageType::Water => statistics.water_damage.raw(),
|
||||
DamageType::Physical => statistics.physical_damage.raw(),
|
||||
};
|
||||
|
||||
// damage from addons multiplied with level
|
||||
let addon_damage = self.addons.damage() * self.level;
|
||||
|
||||
// sum up
|
||||
ability_base_damage + (stats_damage * self.level) + addon_damage
|
||||
}
|
||||
|
||||
pub fn into_persistent(&self) -> String {
|
||||
let mut base = format!("{}|{}|{}", self.ability().name(), self.rarity(), self.level);
|
||||
|
||||
for addon in self.addons.iter().flatten() {
|
||||
base = format!("{}|{}_{}", base, addon.addon_type(), addon.rarity());
|
||||
}
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
pub fn from_persistent<'a>(
|
||||
mut split: impl Iterator<Item = &'a str>,
|
||||
item_system: &ItemSystem<A>,
|
||||
) -> Result<Self> {
|
||||
let name = split.next().unwrap();
|
||||
let rarity = Rarities::from_str(split.next().unwrap())?;
|
||||
let level = u32::from_str(split.next().unwrap())?;
|
||||
|
||||
let mut addons = Vec::new();
|
||||
|
||||
for addon in split {
|
||||
let mut addon_split = addon.split('_');
|
||||
|
||||
let addon_type = AbilityAddonTypes::from_str(addon_split.next().unwrap())?;
|
||||
let addon_rarity = Rarities::from_str(addon_split.next().unwrap())?;
|
||||
|
||||
addons.push(Some(item_system.addon(addon_rarity, addon_type)));
|
||||
}
|
||||
|
||||
Ok(item_system.ability_book(name, rarity, addons, level))
|
||||
}
|
||||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
statistics: &Statistics,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let gui = GuiBuilder::from_str(
|
||||
gui_handler,
|
||||
include_str!("../../resources/book_snippet.xml"),
|
||||
)?;
|
||||
|
||||
let ability_name: Arc<Label> = gui.element("ability_name")?;
|
||||
let rarity_label: Arc<Label> = gui.element("rarity_label")?;
|
||||
let inspector_grid: Arc<Grid> = gui.element("item_grid")?;
|
||||
let ability_icon: Arc<Icon> = gui.element("ability_icon")?;
|
||||
let slot_info: Arc<Label> = gui.element("slot_info")?;
|
||||
let level: Arc<Label> = gui.element("level")?;
|
||||
|
||||
inspector_grid.change_position_unscaled(position.0, position.1)?;
|
||||
|
||||
ability_icon.set_icon(&self.icon())?;
|
||||
ability_name.set_text(self.ability().name())?;
|
||||
rarity_label.set_text(&format!("{}", self.rarity()))?;
|
||||
level.set_text(&format!("Lvl: {}", self.level()))?;
|
||||
|
||||
slot_info.set_text(&format!(
|
||||
"Slots: {}/{}",
|
||||
self.attached_count(),
|
||||
self.addons().len()
|
||||
))?;
|
||||
|
||||
let mana_costs: Arc<Label> = gui.element("mana_costs")?;
|
||||
let damage: Arc<Label> = gui.element("damage")?;
|
||||
let cooldown: Arc<Label> = gui.element("cooldown")?;
|
||||
|
||||
mana_costs.set_text(self.mana_cost())?;
|
||||
damage.set_text(self.damage(statistics))?;
|
||||
damage.set_text_color(self.ability.damage_type().into())?;
|
||||
cooldown.set_text(format!(
|
||||
"{:.1} s",
|
||||
self.ability.cool_down().as_secs_f32() * (1.0 - self.addons().cool_down_reduction())
|
||||
))?;
|
||||
|
||||
Ok(Tooltip::new(inspector_grid, gui, gui_handler.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability> Debug for AbilityBook<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AbilityBook")
|
||||
.field("rarity", &self.rarity)
|
||||
.field("attached_addons", &self.attached_count())
|
||||
.field("addons", &self.addons)
|
||||
.field("last_cast", &self.last_cast)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability> PartialEq for AbilityBook<A> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ability.name() == other.ability.name()
|
||||
&& self.rarity == other.rarity
|
||||
&& self.addons == other.addons
|
||||
&& self.level == other.level
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ability> Storable for AbilityBook<A> {
|
||||
fn rarity(&self) -> Rarities {
|
||||
self.rarity
|
||||
}
|
||||
|
||||
fn icon(&self) -> Arc<Image> {
|
||||
self.icon.clone()
|
||||
}
|
||||
}
|
|
@ -1,356 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
components::{
|
||||
attributes::Attributes,
|
||||
inventory::Storable,
|
||||
statistic_types::{
|
||||
AgilityStatisticTypes, IntelligenceStatisticTypes, StatisticType,
|
||||
StrengthStatisticTypes,
|
||||
},
|
||||
},
|
||||
config::items::ItemSettings,
|
||||
};
|
||||
|
||||
use super::{ability_book::Ability, ItemSlots, ItemSystem, Jewel, Rarities, Tooltip};
|
||||
|
||||
const ITEM_SNIPPETS: [&'static str; 8] = [
|
||||
include_str!("../../resources/items/slots_0.xml"),
|
||||
include_str!("../../resources/items/slots_1.xml"),
|
||||
include_str!("../../resources/items/slots_2.xml"),
|
||||
include_str!("../../resources/items/slots_3.xml"),
|
||||
include_str!("../../resources/items/slots_4.xml"),
|
||||
include_str!("../../resources/items/slots_5.xml"),
|
||||
include_str!("../../resources/items/slots_6.xml"),
|
||||
include_str!("../../resources/items/slots_7.xml"),
|
||||
];
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ItemAffix {
|
||||
Socket(Option<Jewel>),
|
||||
Stat(StatisticType),
|
||||
}
|
||||
|
||||
impl ItemAffix {
|
||||
pub fn from_persistent<'a>(
|
||||
affix_type: &str,
|
||||
split: &mut impl Iterator<Item = &'a str>,
|
||||
) -> Result<Self> {
|
||||
Ok(match affix_type {
|
||||
"socket" => {
|
||||
let socket_content = split.next().unwrap();
|
||||
|
||||
match socket_content {
|
||||
"empty" => ItemAffix::Socket(None),
|
||||
"some" => ItemAffix::Socket(Some(Jewel::from_persistent(split)?)),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
"stat" => ItemAffix::Stat(StatisticType::from_str(split.next().unwrap())?),
|
||||
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
fn squash<'a>(v: impl Iterator<Item = &'a Self>) -> (Vec<StatisticType>, Vec<Option<Jewel>>) {
|
||||
let mut jewels = Vec::new();
|
||||
let mut stats = Vec::new();
|
||||
|
||||
for affix in v {
|
||||
match affix {
|
||||
ItemAffix::Socket(j) => jewels.push(j.clone()),
|
||||
ItemAffix::Stat(s) => Self::insert_stat(s, &mut stats),
|
||||
}
|
||||
}
|
||||
|
||||
(stats, jewels)
|
||||
}
|
||||
|
||||
fn insert_stat(val: &StatisticType, vec: &mut Vec<StatisticType>) {
|
||||
match vec
|
||||
.iter_mut()
|
||||
.find(|stat| StatisticType::same_type(stat, val))
|
||||
{
|
||||
Some(stat) => {
|
||||
*stat += val.clone();
|
||||
}
|
||||
None => vec.push(val.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ItemAffix {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ItemAffix::Socket(jewel) => match jewel {
|
||||
Some(jewel) => write!(f, "socket|some|{}", jewel.into_persistent()),
|
||||
None => write!(f, "socket|empty"),
|
||||
},
|
||||
ItemAffix::Stat(stat) => write!(f, "stat|{}", stat),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ItemAffix {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
let mut split = s.split('|');
|
||||
let affix_type = split.next().unwrap();
|
||||
|
||||
Self::from_persistent(affix_type, &mut split)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Item {
|
||||
pub rarity: Rarities,
|
||||
pub slot: ItemSlots,
|
||||
pub level: u32,
|
||||
|
||||
pub attributes: Attributes,
|
||||
|
||||
pub affixes: Vec<ItemAffix>,
|
||||
|
||||
pub icon: Arc<Image>,
|
||||
}
|
||||
|
||||
impl Item {
|
||||
const INSPECTOR_OFFSET: usize = 3;
|
||||
|
||||
pub fn empty(rarity: Rarities, slot: ItemSlots, level: u32, icon: Arc<Image>) -> Self {
|
||||
Self {
|
||||
rarity,
|
||||
slot,
|
||||
level,
|
||||
|
||||
attributes: Attributes::zero(),
|
||||
|
||||
affixes: Vec::new(),
|
||||
|
||||
icon,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_persistent(&self) -> String {
|
||||
let mut base = format!(
|
||||
"{}|{}|{}|{}|{}|{}",
|
||||
self.slot,
|
||||
self.rarity,
|
||||
self.level,
|
||||
self.attributes.strength().raw(),
|
||||
self.attributes.agility().raw(),
|
||||
self.attributes.intelligence().raw(),
|
||||
);
|
||||
|
||||
for affix in self.affixes.iter() {
|
||||
base = format!("{base}|{affix}");
|
||||
}
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
pub fn from_persistent<'a, A: Ability>(
|
||||
mut split: impl Iterator<Item = &'a str>,
|
||||
item_system: &ItemSystem<A>,
|
||||
) -> Result<Self> {
|
||||
let slot = ItemSlots::from_str(split.next().unwrap())?;
|
||||
let rarity = Rarities::from_str(split.next().unwrap())?;
|
||||
let level = split.next().unwrap().parse::<u32>()?;
|
||||
let strength = split.next().unwrap().parse::<u32>()?;
|
||||
let agility = split.next().unwrap().parse::<u32>()?;
|
||||
let intelligence = split.next().unwrap().parse::<u32>()?;
|
||||
|
||||
let mut affixes = Vec::new();
|
||||
|
||||
while let Some(affix_type) = split.next() {
|
||||
affixes.push({
|
||||
let mut affix = ItemAffix::from_persistent(affix_type, &mut split)?;
|
||||
|
||||
if let ItemAffix::Socket(Some(jewel)) = &mut affix {
|
||||
jewel.icon =
|
||||
Some(item_system.jewel_icon(jewel.rarity, jewel.level, jewel.attribute));
|
||||
}
|
||||
|
||||
affix
|
||||
});
|
||||
}
|
||||
|
||||
let attributes = Attributes::load(strength, agility, intelligence);
|
||||
|
||||
Ok(item_system.item(rarity, slot, level, attributes, affixes))
|
||||
}
|
||||
|
||||
pub fn randomize_attributes(&mut self, item_settings: &ItemSettings) {
|
||||
// sum of stats is the level
|
||||
self.attributes
|
||||
.randomize((self.level as f32 * item_settings.general.attribute_multiplier) as u32);
|
||||
}
|
||||
|
||||
fn stat_type(&self, attribute_number: u32) -> Option<StatisticType> {
|
||||
if attribute_number <= self.attributes.strength().raw()
|
||||
&& self.attributes.strength().raw() != 0
|
||||
{
|
||||
return Some(StrengthStatisticTypes::random().into());
|
||||
} else if attribute_number
|
||||
<= (self.attributes.strength().raw() + self.attributes.agility().raw())
|
||||
&& self.attributes.agility().raw() != 0
|
||||
{
|
||||
return Some(AgilityStatisticTypes::random().into());
|
||||
} else if self.attributes.intelligence().raw() != 0 {
|
||||
return Some(IntelligenceStatisticTypes::random().into());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn randomize_stats(&mut self, item_settings: &ItemSettings) {
|
||||
self.affixes = (0..item_settings.rarity_slot_settings.from_rarity(self.rarity))
|
||||
.map(|_| {
|
||||
if Random::range_f32(0.0, 1.0) < 0.05 {
|
||||
ItemAffix::Socket(None)
|
||||
} else {
|
||||
let mut stat_type = None;
|
||||
|
||||
while stat_type.is_none() {
|
||||
stat_type = self.stat_type(Random::range(0, self.attributes.sum()));
|
||||
}
|
||||
|
||||
let mut stat_type = stat_type.unwrap();
|
||||
|
||||
stat_type.apply_for_item(&self.attributes, item_settings);
|
||||
|
||||
ItemAffix::Stat(stat_type)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
attributes: &Attributes,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let (stats, jewels) = ItemAffix::squash(self.affixes.iter());
|
||||
let count = stats.len() + jewels.len();
|
||||
|
||||
let inspector_snippet = GuiBuilder::from_str(gui_handler, &ITEM_SNIPPETS[count])?;
|
||||
|
||||
let item_icon: Arc<Icon> = inspector_snippet.element("item_icon")?;
|
||||
let slot_label: Arc<Label> = inspector_snippet.element("slot_label")?;
|
||||
let level_label: Arc<Label> = inspector_snippet.element("level_label")?;
|
||||
let rarity_label: Arc<Label> = inspector_snippet.element("rarity_label")?;
|
||||
let strength_field: Arc<Label> = inspector_snippet.element("strength_field")?;
|
||||
let agility_field: Arc<Label> = inspector_snippet.element("agility_field")?;
|
||||
let intelligence_field: Arc<Label> = inspector_snippet.element("intelligence_field")?;
|
||||
let inspector_grid: Arc<Grid> = inspector_snippet.element("item_grid")?;
|
||||
|
||||
inspector_grid.change_position_unscaled(position.0, position.1)?;
|
||||
|
||||
item_icon.set_icon(&self.icon)?;
|
||||
slot_label.set_text(&format!("{}", self.slot))?;
|
||||
level_label.set_text(&format!("Lvl {}", self.level))?;
|
||||
rarity_label.set_text(&format!("{}", self.rarity))?;
|
||||
strength_field.set_text(&format!("{}", self.attributes.strength().raw()))?;
|
||||
agility_field.set_text(&format!("{}", self.attributes.agility().raw()))?;
|
||||
intelligence_field.set_text(&format!("{}", self.attributes.intelligence().raw()))?;
|
||||
|
||||
let too_low_stat_background = FillTypeInfo::Element(
|
||||
ElementDescriptor::new(Color::try_from("#AB7474")?, Color::try_from("#824040")?, 2),
|
||||
DisplayableFillType::Expand,
|
||||
);
|
||||
|
||||
if attributes.strength().raw() < self.attributes.strength().raw() {
|
||||
strength_field.set_background(too_low_stat_background.clone())?;
|
||||
}
|
||||
|
||||
if attributes.agility().raw() < self.attributes.agility().raw() {
|
||||
agility_field.set_background(too_low_stat_background.clone())?;
|
||||
}
|
||||
|
||||
if attributes.intelligence().raw() < self.attributes.intelligence().raw() {
|
||||
intelligence_field.set_background(too_low_stat_background)?;
|
||||
}
|
||||
|
||||
let mut index = Self::INSPECTOR_OFFSET;
|
||||
|
||||
for stat in stats {
|
||||
let stat_type_snippet = GuiSnippet::from_str(
|
||||
gui_handler,
|
||||
include_str!("../../resources/stat_type_snippet.xml"),
|
||||
)?;
|
||||
|
||||
let stat_type_label: Arc<Label> = stat_type_snippet.element("stat_type")?;
|
||||
let stat_value_label: Arc<Label> = stat_type_snippet.element("stat_value")?;
|
||||
|
||||
stat_type_label.set_text(&format!("{}", stat))?;
|
||||
stat_value_label.set_text(&stat.display_value())?;
|
||||
|
||||
inspector_grid.attach(stat_type_snippet, 0, index, 1, 1)?;
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
for jewel in jewels {
|
||||
let socket_snippet = GuiSnippet::from_str(
|
||||
gui_handler,
|
||||
include_str!("../../resources/item_socket_snippet.xml"),
|
||||
)?;
|
||||
|
||||
let socket_icon: Arc<Icon> = socket_snippet.element("socket_icon")?;
|
||||
let stat_type: Arc<Label> = socket_snippet.element("stat_type")?;
|
||||
|
||||
match jewel {
|
||||
Some(jewel) => {
|
||||
socket_icon.set_icon(&jewel.icon())?;
|
||||
stat_type.set_text(format!("{}", jewel.stat))?;
|
||||
}
|
||||
None => {
|
||||
socket_icon.set_icon(
|
||||
&Image::from_slice(include_bytes!("../../resources/circle.png"))?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(gui_handler.device())?)
|
||||
.build(gui_handler.device(), gui_handler.queue())?,
|
||||
)?;
|
||||
|
||||
stat_type.set_text("Empty Socket")?
|
||||
}
|
||||
}
|
||||
|
||||
inspector_grid.attach(socket_snippet, 0, index, 1, 1)?;
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
Ok(Tooltip::new(
|
||||
inspector_grid,
|
||||
inspector_snippet,
|
||||
gui_handler.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Item {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.rarity == other.rarity
|
||||
&& self.slot == other.slot
|
||||
&& self.level == other.level
|
||||
&& self.attributes == other.attributes
|
||||
&& self.affixes == other.affixes
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for Item {
|
||||
fn rarity(&self) -> Rarities {
|
||||
self.rarity
|
||||
}
|
||||
|
||||
fn icon(&self) -> Arc<Image> {
|
||||
self.icon.clone()
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::{fmt, slice::Iter};
|
||||
|
||||
use crate::config::items::ItemSettings;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum ItemSlots {
|
||||
Helmet,
|
||||
ChestPlate,
|
||||
Belt,
|
||||
Gloves,
|
||||
Boots,
|
||||
Ring,
|
||||
Amulet,
|
||||
MainHand,
|
||||
OffHand,
|
||||
}
|
||||
|
||||
impl ItemSlots {
|
||||
const COUNT: u32 = 9;
|
||||
|
||||
pub fn iter() -> Iter<'static, ItemSlots> {
|
||||
use ItemSlots::*;
|
||||
static SLOTS: [ItemSlots; ItemSlots::COUNT as usize] = [
|
||||
Helmet, ChestPlate, Belt, Gloves, Boots, Ring, Amulet, MainHand, OffHand,
|
||||
];
|
||||
SLOTS.iter()
|
||||
}
|
||||
|
||||
pub fn random() -> Self {
|
||||
let n = Random::range(0, Self::COUNT);
|
||||
|
||||
n.into()
|
||||
}
|
||||
|
||||
pub fn get_path<'a>(&self, item_settings: &'a ItemSettings) -> &'a AssetPath {
|
||||
use ItemSlots::*;
|
||||
match self {
|
||||
Helmet => &item_settings.icon_paths.helmet,
|
||||
ChestPlate => &item_settings.icon_paths.chest,
|
||||
Belt => &item_settings.icon_paths.belt,
|
||||
Gloves => &item_settings.icon_paths.gloves,
|
||||
Boots => &item_settings.icon_paths.boots,
|
||||
Ring => &item_settings.icon_paths.ring,
|
||||
Amulet => &item_settings.icon_paths.amulet,
|
||||
MainHand => &item_settings.icon_paths.main_hand,
|
||||
OffHand => &item_settings.icon_paths.off_hand,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for ItemSlots {
|
||||
fn from(n: u32) -> Self {
|
||||
use ItemSlots::*;
|
||||
|
||||
match n {
|
||||
0 => Helmet,
|
||||
1 => ChestPlate,
|
||||
2 => Belt,
|
||||
3 => Gloves,
|
||||
4 => Boots,
|
||||
5 => Ring,
|
||||
6 => Amulet,
|
||||
7 => MainHand,
|
||||
8 => OffHand,
|
||||
|
||||
_ => panic!("can only convert number below COUNT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ItemSlots {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"Helmet" => Ok(Self::Helmet),
|
||||
"Chest Plate" => Ok(Self::ChestPlate),
|
||||
"Belt" => Ok(Self::Belt),
|
||||
"Gloves" => Ok(Self::Gloves),
|
||||
"Boots" => Ok(Self::Boots),
|
||||
"Ring" => Ok(Self::Ring),
|
||||
"Amulet" => Ok(Self::Amulet),
|
||||
"Main Hand" => Ok(Self::MainHand),
|
||||
"Off Hand" => Ok(Self::OffHand),
|
||||
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Failed parsing ItemSlots from {}",
|
||||
s
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ItemSlots {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use ItemSlots::*;
|
||||
|
||||
match self {
|
||||
Helmet => write!(f, "Helmet"),
|
||||
ChestPlate => write!(f, "Chest Plate"),
|
||||
Belt => write!(f, "Belt"),
|
||||
Gloves => write!(f, "Gloves"),
|
||||
Boots => write!(f, "Boots"),
|
||||
Ring => write!(f, "Ring"),
|
||||
Amulet => write!(f, "Amulet"),
|
||||
MainHand => write!(f, "Main Hand"),
|
||||
OffHand => write!(f, "Off Hand"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,791 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use image::{DynamicImage, ImageBuffer, Pixel, Rgba, RgbaImage, imageops::FilterType};
|
||||
|
||||
use crate::components::attributes::{Attribute, Attributes};
|
||||
use crate::components::inventory::Storable;
|
||||
use crate::components::statistic_types::{
|
||||
AgilityStatisticTypes, IntelligenceStatisticTypes, StatisticType, StrengthStatisticTypes,
|
||||
};
|
||||
use crate::config::abilities::AbilitySettings;
|
||||
use crate::config::attributes::AttributeSettings;
|
||||
use crate::config::items::ItemSettings;
|
||||
|
||||
use super::ability_addon::{AbilityAddon, AbilityAddonTypes};
|
||||
use super::ability_book::{Ability, AbilityBook};
|
||||
use super::{Item, ItemAffix, ItemSlots, Jewel, Rarities};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Loot<A: Ability> {
|
||||
Item(Item),
|
||||
AbilityBook(AbilityBook<A>),
|
||||
AbilityAddOn(AbilityAddon),
|
||||
Jewel(Jewel),
|
||||
}
|
||||
|
||||
impl<A: Ability> Loot<A> {
|
||||
pub fn storable(&self) -> &dyn Storable {
|
||||
match self {
|
||||
Loot::Item(item) => item,
|
||||
Loot::AbilityBook(book) => book,
|
||||
Loot::AbilityAddOn(addon) => addon,
|
||||
Loot::Jewel(jewel) => jewel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ItemSystem<A: Ability> {
|
||||
pub item_settings: ItemSettings,
|
||||
ability_settings: AbilitySettings,
|
||||
|
||||
item_icon_combinations: HashMap<(Rarities, ItemSlots), Arc<Image>>,
|
||||
ability_icon_combinations: HashMap<(Rarities, String), Arc<Image>>,
|
||||
addon_icon_combinations: HashMap<(Rarities, AbilityAddonTypes), Arc<Image>>,
|
||||
jewel_icon_combinations: HashMap<(Rarities, u32, Attribute), Arc<Image>>,
|
||||
|
||||
abilities: Vec<A>,
|
||||
}
|
||||
|
||||
impl<A: Ability> ItemSystem<A> {
|
||||
pub fn new(
|
||||
context: &Context,
|
||||
item_settings: &ItemSettings,
|
||||
ability_settings: &AbilitySettings,
|
||||
attribute_settings: &AttributeSettings,
|
||||
ability_directory: &AssetPath,
|
||||
) -> Result<Self> {
|
||||
// verify that drop chances sum up to 1.0
|
||||
item_settings.rarity_drop_rates.verify();
|
||||
|
||||
let abilities = search_dir_recursively(&ability_directory.full_path(), ".abil")?
|
||||
.into_iter()
|
||||
.map(|path| A::create(context, path))
|
||||
.collect::<Result<Vec<A>>>()?;
|
||||
|
||||
let (
|
||||
item_icon_combinations,
|
||||
ability_icon_combinations,
|
||||
addon_icon_combinations,
|
||||
jewel_icon_combinations,
|
||||
) = {
|
||||
// images for item slots
|
||||
let mut slot_images = Vec::new();
|
||||
|
||||
for slot in ItemSlots::iter() {
|
||||
let path = slot.get_path(item_settings);
|
||||
|
||||
if !path.is_empty() {
|
||||
slot_images.push((*slot, Self::dyn_image(path)?));
|
||||
}
|
||||
}
|
||||
|
||||
// image for ability book
|
||||
let mut ability_images = Vec::new();
|
||||
|
||||
for loader in abilities.iter() {
|
||||
let path = loader.icon_path();
|
||||
|
||||
if !path.is_empty() {
|
||||
ability_images.push((loader.name().to_string(), Self::dyn_image(path)?));
|
||||
}
|
||||
}
|
||||
|
||||
// images for ability addons
|
||||
let mut ability_addon_images = Vec::new();
|
||||
|
||||
for addon in AbilityAddonTypes::iter() {
|
||||
let path = addon.get_path(ability_settings);
|
||||
|
||||
if !path.is_empty() {
|
||||
ability_addon_images.push((*addon, Self::dyn_image(path)?));
|
||||
}
|
||||
}
|
||||
|
||||
// images for jewels
|
||||
let mut jewel_images = Vec::new();
|
||||
jewel_images.push((0, Self::dyn_image(&item_settings.jewel_paths.first)?));
|
||||
jewel_images.push((1, Self::dyn_image(&item_settings.jewel_paths.second)?));
|
||||
jewel_images.push((2, Self::dyn_image(&item_settings.jewel_paths.third)?));
|
||||
jewel_images.push((3, Self::dyn_image(&item_settings.jewel_paths.fourth)?));
|
||||
|
||||
// resize all icons to have the same size
|
||||
let (
|
||||
item_icons,
|
||||
base_background,
|
||||
ability_icons,
|
||||
ability_addon_icons,
|
||||
addon_background,
|
||||
jewel_icons,
|
||||
) = Self::equalize_images(
|
||||
slot_images,
|
||||
Self::dyn_image(&item_settings.icon_paths.background)?,
|
||||
ability_images,
|
||||
ability_addon_images,
|
||||
Self::dyn_image(&ability_settings.icons.addon_background)?,
|
||||
jewel_images,
|
||||
item_settings,
|
||||
);
|
||||
|
||||
// create item icon combination for every slot and rarity
|
||||
let mut item_icon_combinations = HashMap::new();
|
||||
|
||||
for (slot, icon) in item_icons.iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
let final_image = Self::blend_background(
|
||||
icon,
|
||||
&rarity.apply_color::<A>(
|
||||
&base_background,
|
||||
&item_settings.rarity_color_settings,
|
||||
),
|
||||
);
|
||||
|
||||
let (width, height) = final_image.dimensions();
|
||||
|
||||
item_icon_combinations.insert(
|
||||
(*rarity, *slot),
|
||||
Self::create_icon(
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// create book icon combination for every rarity
|
||||
let mut ability_icon_combinations = HashMap::new();
|
||||
|
||||
for (name, image) in ability_icons.iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
let final_image = Self::blend_background(
|
||||
&image,
|
||||
&rarity.apply_color::<A>(
|
||||
&base_background,
|
||||
&item_settings.rarity_color_settings,
|
||||
),
|
||||
);
|
||||
|
||||
let (width, height) = final_image.dimensions();
|
||||
|
||||
ability_icon_combinations.insert(
|
||||
(*rarity, name.clone()),
|
||||
Self::create_icon(
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// create addon icon combinatiosn for every rarity and addon type
|
||||
let mut addon_icon_combinations = HashMap::new();
|
||||
|
||||
for (addon_type, icon) in ability_addon_icons.iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
let final_image = Self::blend_apply(
|
||||
&rarity.apply_color::<A>(
|
||||
&addon_background,
|
||||
&item_settings.rarity_color_settings,
|
||||
),
|
||||
icon,
|
||||
|rarity_color, icon_color| {
|
||||
// let (r1, g1, b1, a1) = rarity_color.channels4();
|
||||
let c2 = icon_color.channels();
|
||||
|
||||
if c2[3] == 0 {
|
||||
rarity_color
|
||||
} else {
|
||||
Rgba([c2[0], c2[1], c2[2], 255])
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let (width, height) = final_image.dimensions();
|
||||
|
||||
addon_icon_combinations.insert(
|
||||
(*rarity, *addon_type),
|
||||
Self::create_icon(
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// create jewel icons for every rarity
|
||||
let mut jewel_icon_combinations = HashMap::new();
|
||||
|
||||
for (level, icon) in jewel_icons.iter() {
|
||||
for attribute in Attribute::iter() {
|
||||
let attribute_icon = attribute
|
||||
.apply_color::<A>(&icon, &attribute_settings.attribute_color_settings);
|
||||
|
||||
for rarity in Rarities::iter() {
|
||||
let final_image = Self::blend_background(
|
||||
&attribute_icon,
|
||||
&rarity.apply_color::<A>(
|
||||
&base_background,
|
||||
&item_settings.rarity_color_settings,
|
||||
),
|
||||
);
|
||||
|
||||
let (width, height) = final_image.dimensions();
|
||||
|
||||
jewel_icon_combinations.insert(
|
||||
(*rarity, *level + 1, *attribute),
|
||||
Self::create_icon(
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
item_icon_combinations,
|
||||
ability_icon_combinations,
|
||||
addon_icon_combinations,
|
||||
jewel_icon_combinations,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(ItemSystem {
|
||||
item_settings: item_settings.clone(),
|
||||
ability_settings: ability_settings.clone(),
|
||||
|
||||
item_icon_combinations,
|
||||
ability_icon_combinations,
|
||||
addon_icon_combinations,
|
||||
jewel_icon_combinations,
|
||||
|
||||
abilities,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn create_icon(
|
||||
device: &Arc<Device>,
|
||||
queue: &Arc<Mutex<Queue>>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: Vec<u8>,
|
||||
) -> Result<Arc<Image>> {
|
||||
Image::from_raw(data, width, height)
|
||||
.format(VK_FORMAT_R8G8B8A8_UNORM)
|
||||
.attach_sampler(Sampler::nearest_sampler().build(device)?)
|
||||
.build(device, queue)
|
||||
}
|
||||
|
||||
/// only here for debugging
|
||||
pub fn get_legendary_random_loot(&self, level: u32) -> Loot<A> {
|
||||
// decide which type of loot gets dropped
|
||||
let loot_type_p = Random::range_f32(0.0, 1.0);
|
||||
let rarity = Rarities::Legendary;
|
||||
|
||||
// decide if an item gets dropped
|
||||
if loot_type_p >= 0.0 && loot_type_p <= self.item_settings.type_drop_rates.item {
|
||||
Loot::Item(self.random_item(level, rarity))
|
||||
}
|
||||
// decide if an ability addon gets dropped
|
||||
else if loot_type_p > self.item_settings.type_drop_rates.item
|
||||
&& loot_type_p
|
||||
<= self.item_settings.type_drop_rates.item
|
||||
+ self.item_settings.type_drop_rates.ability_addon
|
||||
{
|
||||
Loot::AbilityAddOn(self.random_ability_addon(rarity))
|
||||
}
|
||||
// drop an ability book
|
||||
else {
|
||||
if Coin::flip(0.5) {
|
||||
Loot::AbilityBook(self.random_ability_book(rarity))
|
||||
} else {
|
||||
Loot::Jewel(self.random_jewel(level, rarity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_random_loot(
|
||||
&self,
|
||||
level: u32,
|
||||
drop_chance_multiplier: Option<f32>,
|
||||
) -> Option<Loot<A>> {
|
||||
let drop_chance = match drop_chance_multiplier {
|
||||
Some(multiplier) => multiplier * self.item_settings.general.drop_chance,
|
||||
None => self.item_settings.general.drop_chance,
|
||||
};
|
||||
|
||||
// decide if loot gets dropped at all
|
||||
if !Coin::flip(drop_chance) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// decide which type of loot gets dropped
|
||||
let loot_type_p = Random::range_f32(0.0, 1.0);
|
||||
|
||||
let rarity = Rarities::random(&self.item_settings, level);
|
||||
|
||||
// decide if an item gets dropped
|
||||
if loot_type_p >= 0.0 && loot_type_p <= self.item_settings.type_drop_rates.item {
|
||||
Some(Loot::Item(self.random_item(level, rarity)))
|
||||
}
|
||||
// decide if an ability addon gets dropped
|
||||
else if loot_type_p > self.item_settings.type_drop_rates.item
|
||||
&& loot_type_p
|
||||
<= self.item_settings.type_drop_rates.item
|
||||
+ self.item_settings.type_drop_rates.ability_addon
|
||||
{
|
||||
Some(Loot::AbilityAddOn(self.random_ability_addon(rarity)))
|
||||
}
|
||||
// drop an ability book
|
||||
else {
|
||||
if Coin::flip(0.5) {
|
||||
Some(Loot::AbilityBook(self.random_ability_book(rarity)))
|
||||
} else {
|
||||
Some(Loot::Jewel(self.random_jewel(1, rarity)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_ability_book(&self, rarity: Rarities) -> AbilityBook<A> {
|
||||
let ability = self.random_ability();
|
||||
let ability_icon = self.ability_icon(ability.name(), rarity);
|
||||
|
||||
AbilityBook::new(ability, ability_icon, rarity, &self.ability_settings)
|
||||
}
|
||||
|
||||
pub fn ability_addon(
|
||||
&self,
|
||||
rarity: Rarities,
|
||||
mut addon_type: AbilityAddonTypes,
|
||||
) -> AbilityAddon {
|
||||
let icon = self.addon_icon(rarity, addon_type);
|
||||
|
||||
addon_type.apply_rarity(rarity, &self.ability_settings);
|
||||
|
||||
AbilityAddon::new(rarity, addon_type, icon)
|
||||
}
|
||||
|
||||
pub fn random_ability_addon(&self, rarity: Rarities) -> AbilityAddon {
|
||||
self.ability_addon(rarity, AbilityAddonTypes::random())
|
||||
}
|
||||
|
||||
pub fn random_item(&self, level: u32, rarity: Rarities) -> Item {
|
||||
let slot = ItemSlots::random();
|
||||
|
||||
let mut item = Item::empty(rarity, slot, level, self.item_icon(rarity, slot));
|
||||
|
||||
item.randomize_attributes(&self.item_settings);
|
||||
item.randomize_stats(&self.item_settings);
|
||||
|
||||
item
|
||||
}
|
||||
|
||||
pub fn random_jewel(&self, level: u32, rarity: Rarities) -> Jewel {
|
||||
let attribute = Attribute::random();
|
||||
|
||||
let stat = match attribute {
|
||||
Attribute::Agility => AgilityStatisticTypes::random()
|
||||
.apply_for_jewel(level, rarity, &self.item_settings)
|
||||
.into(),
|
||||
Attribute::Intelligence => IntelligenceStatisticTypes::random()
|
||||
.apply_for_jewel(level, rarity, &self.item_settings)
|
||||
.into(),
|
||||
Attribute::Strength => StrengthStatisticTypes::random()
|
||||
.apply_for_jewel(level, rarity, &self.item_settings)
|
||||
.into(),
|
||||
};
|
||||
|
||||
self.jewel(rarity, level, attribute, stat)
|
||||
}
|
||||
|
||||
pub fn item(
|
||||
&self,
|
||||
rarity: Rarities,
|
||||
slot: ItemSlots,
|
||||
level: u32,
|
||||
attributes: Attributes,
|
||||
mut affixes: Vec<ItemAffix>,
|
||||
) -> Item {
|
||||
for affix in affixes.iter_mut() {
|
||||
match affix {
|
||||
ItemAffix::Socket(_) => (),
|
||||
ItemAffix::Stat(stat) => stat.apply_for_item(&attributes, &self.item_settings),
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
rarity,
|
||||
slot,
|
||||
level,
|
||||
attributes,
|
||||
affixes,
|
||||
|
||||
icon: self.item_icon(rarity, slot),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addon(&self, rarity: Rarities, addon_type: AbilityAddonTypes) -> AbilityAddon {
|
||||
AbilityAddon::load(
|
||||
addon_type,
|
||||
rarity,
|
||||
self.addon_icon(rarity, addon_type),
|
||||
&self.ability_settings,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ability_book(
|
||||
&self,
|
||||
ability_name: &str,
|
||||
rarity: Rarities,
|
||||
addons: Vec<Option<AbilityAddon>>,
|
||||
level: u32,
|
||||
) -> AbilityBook<A> {
|
||||
AbilityBook::load(
|
||||
self.find_ability(ability_name),
|
||||
self.ability_icon(ability_name, rarity),
|
||||
rarity,
|
||||
addons,
|
||||
&self.ability_settings,
|
||||
level,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn jewel(
|
||||
&self,
|
||||
rarity: Rarities,
|
||||
level: u32,
|
||||
attribute: Attribute,
|
||||
stat: StatisticType,
|
||||
) -> Jewel {
|
||||
Jewel {
|
||||
rarity,
|
||||
level,
|
||||
attribute,
|
||||
|
||||
stat,
|
||||
|
||||
icon: Some(self.jewel_icon(rarity, level, attribute)),
|
||||
}
|
||||
}
|
||||
|
||||
fn dyn_image(path: &AssetPath) -> Result<DynamicImage> {
|
||||
Ok(image::open(&path.full_path())?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn item_icon(&self, rarity: Rarities, slot: ItemSlots) -> Arc<Image> {
|
||||
self.item_icon_combinations
|
||||
.get(&(rarity, slot))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"no icon for combination ({:?} {:?}) present\navailable: {:#?}",
|
||||
rarity,
|
||||
slot,
|
||||
self.item_icon_combinations.keys()
|
||||
)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn addon_icon(&self, rarity: Rarities, addon_type: AbilityAddonTypes) -> Arc<Image> {
|
||||
let addon_type = addon_type.into_zero();
|
||||
|
||||
self.addon_icon_combinations
|
||||
.get(&(rarity, addon_type))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"no addon icon present for rarity ({}) and addon type ({:?})\navailable: {:#?}",
|
||||
rarity,
|
||||
addon_type,
|
||||
self.addon_icon_combinations.keys()
|
||||
)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ability_icon(&self, name: impl ToString, rarity: Rarities) -> Arc<Image> {
|
||||
self.ability_icon_combinations
|
||||
.get(&(rarity, name.to_string()))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"no book icon present for rarity ({})\navailable: {:#?}",
|
||||
rarity,
|
||||
self.ability_icon_combinations.keys()
|
||||
)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn jewel_icon(&self, rarity: Rarities, level: u32, attribute: Attribute) -> Arc<Image> {
|
||||
self.jewel_icon_combinations
|
||||
.get(&(rarity, level, attribute))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"no jewel icon present for rarity ({})\navailable: {:#?}",
|
||||
rarity,
|
||||
self.jewel_icon_combinations.keys()
|
||||
)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn find_ability(&self, name: &str) -> A {
|
||||
self.abilities
|
||||
.iter()
|
||||
.find(|a| a.name() == name)
|
||||
.unwrap_or_else(|| panic!("no ability with name ({}) found", name))
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn random_ability(&self) -> A {
|
||||
let n = Random::range(0, self.abilities.len() as u32);
|
||||
self.abilities[n as usize].clone()
|
||||
}
|
||||
|
||||
fn equalize_images(
|
||||
item_icons: Vec<(ItemSlots, DynamicImage)>,
|
||||
background: DynamicImage,
|
||||
ability_images: Vec<(String, DynamicImage)>,
|
||||
addon_icons: Vec<(AbilityAddonTypes, DynamicImage)>,
|
||||
addon_background: DynamicImage,
|
||||
jewel_images: Vec<(u32, DynamicImage)>,
|
||||
item_settings: &ItemSettings,
|
||||
) -> (
|
||||
Vec<(ItemSlots, RgbaImage)>,
|
||||
RgbaImage,
|
||||
Vec<(String, RgbaImage)>,
|
||||
Vec<(AbilityAddonTypes, RgbaImage)>,
|
||||
RgbaImage,
|
||||
Vec<(u32, RgbaImage)>,
|
||||
) {
|
||||
let rgba_background = background
|
||||
.resize(
|
||||
item_settings.general.icon_target_width,
|
||||
item_settings.general.icon_target_height,
|
||||
FilterType::Gaussian,
|
||||
)
|
||||
.to_rgba8();
|
||||
|
||||
let add_on_background = addon_background
|
||||
.resize(
|
||||
item_settings.general.icon_target_width,
|
||||
item_settings.general.icon_target_height,
|
||||
FilterType::Gaussian,
|
||||
)
|
||||
.to_rgba8();
|
||||
|
||||
let icon_results = item_icons
|
||||
.into_iter()
|
||||
.map(|(slot, image)| {
|
||||
(
|
||||
slot,
|
||||
image
|
||||
.resize(
|
||||
item_settings.general.icon_target_width,
|
||||
item_settings.general.icon_target_height,
|
||||
FilterType::Gaussian,
|
||||
)
|
||||
.to_rgba8(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ability_icon = ability_images
|
||||
.into_iter()
|
||||
.map(|(name, image)| {
|
||||
(
|
||||
name,
|
||||
image
|
||||
.resize(
|
||||
item_settings.general.icon_target_width,
|
||||
item_settings.general.icon_target_height,
|
||||
FilterType::Gaussian,
|
||||
)
|
||||
.to_rgba8(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ability_addons = addon_icons
|
||||
.into_iter()
|
||||
.map(|(addon, image)| {
|
||||
(
|
||||
addon,
|
||||
image
|
||||
.resize(
|
||||
item_settings.general.icon_target_width,
|
||||
item_settings.general.icon_target_height,
|
||||
FilterType::Gaussian,
|
||||
)
|
||||
.to_rgba8(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let jewel_results = jewel_images
|
||||
.into_iter()
|
||||
.map(|(n, image)| {
|
||||
(
|
||||
n,
|
||||
image
|
||||
.resize(
|
||||
item_settings.general.icon_target_width,
|
||||
item_settings.general.icon_target_height,
|
||||
FilterType::Gaussian,
|
||||
)
|
||||
.to_rgba8(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
(
|
||||
icon_results,
|
||||
rgba_background,
|
||||
ability_icon,
|
||||
ability_addons,
|
||||
add_on_background,
|
||||
jewel_results,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn blend_background(i1: &RgbaImage, i2: &RgbaImage) -> RgbaImage {
|
||||
let (width, height) = i1.dimensions();
|
||||
let mut target = ImageBuffer::new(width, height);
|
||||
|
||||
for ((s1, s2), t) in i1.pixels().zip(i2.pixels()).zip(target.pixels_mut()) {
|
||||
let mut source_clone = *s1;
|
||||
source_clone.blend(s2);
|
||||
|
||||
*t = source_clone;
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
pub fn blend_apply<F>(i1: &RgbaImage, i2: &RgbaImage, mut apply: F) -> RgbaImage
|
||||
where
|
||||
F: FnMut(Rgba<u8>, &Rgba<u8>) -> Rgba<u8> + Copy,
|
||||
{
|
||||
let (width, height) = i1.dimensions();
|
||||
let mut target = ImageBuffer::new(width, height);
|
||||
|
||||
for ((s1, s2), t) in i1.pixels().zip(i2.pixels()).zip(target.pixels_mut()) {
|
||||
*t = apply(*s1, s2);
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
pub fn apply_color(base_image: &RgbaImage, result_color: Color) -> RgbaImage {
|
||||
let mut result_image = base_image.clone();
|
||||
|
||||
for color in result_image.pixels_mut() {
|
||||
let c = color.channels();
|
||||
*color = Self::blend_mul((c[0], c[1], c[2], c[3]), result_color.into());
|
||||
}
|
||||
|
||||
result_image
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
fn blend_mul((r, g, b, a): (u8, u8, u8, u8), c: [u8; 3]) -> Rgba<u8> {
|
||||
Rgba([
|
||||
Self::mul(r, c[0]),
|
||||
Self::mul(g, c[1]),
|
||||
Self::mul(b, c[2]),
|
||||
a,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
fn mul(l: u8, r: u8) -> u8 {
|
||||
let lhs = l as f32 / 255.0;
|
||||
let rhs = r as f32 / 255.0;
|
||||
|
||||
(lhs * rhs * 255.0) as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_item_icons() {
|
||||
let item_settings = ItemSettings::default();
|
||||
|
||||
let mut slots = Vec::new();
|
||||
|
||||
for slot in ItemSlots::iter() {
|
||||
let path = slot.get_path(&item_settings);
|
||||
|
||||
slots.push((*slot, path));
|
||||
}
|
||||
|
||||
let mut item_icon_combinations = std::collections::HashMap::new();
|
||||
|
||||
for (slot, path) in slots.iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
item_icon_combinations.insert((*slot, *rarity), (*path).clone());
|
||||
}
|
||||
}
|
||||
|
||||
for slot in ItemSlots::iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
item_icon_combinations
|
||||
.get(&(*slot, *rarity))
|
||||
.unwrap_or_else(|| panic!("item not present for {:?} + {}", slot, rarity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_addon_icons() {
|
||||
let ability_settings = AbilitySettings::default();
|
||||
|
||||
let mut addon_types = Vec::new();
|
||||
|
||||
for addon_type in AbilityAddonTypes::iter() {
|
||||
let path = addon_type.get_path(&ability_settings);
|
||||
|
||||
addon_types.push((*addon_type, path));
|
||||
}
|
||||
|
||||
let mut addon_icon_combinations = std::collections::HashMap::new();
|
||||
|
||||
for (addon_type, path) in addon_types.iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
addon_icon_combinations.insert((*rarity, *addon_type), (*path).clone());
|
||||
}
|
||||
}
|
||||
|
||||
for addon_type in AbilityAddonTypes::iter() {
|
||||
for rarity in Rarities::iter() {
|
||||
addon_icon_combinations
|
||||
.get(&(*rarity, *addon_type))
|
||||
.unwrap_or_else(|| panic!("item not present for {:?} + {}", addon_type, rarity));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
use std::{
|
||||
str::{from_utf8, FromStr},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use super::{Rarities, Tooltip};
|
||||
use crate::{
|
||||
components::{attributes::Attribute, inventory::Storable, statistic_types::StatisticType},
|
||||
config::items::ItemSettings,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Jewel {
|
||||
pub rarity: Rarities,
|
||||
pub level: u32,
|
||||
pub attribute: Attribute,
|
||||
|
||||
pub stat: StatisticType,
|
||||
|
||||
pub icon: Option<Arc<Image>>,
|
||||
}
|
||||
|
||||
impl PartialEq for Jewel {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.rarity == other.rarity && self.level == other.level
|
||||
}
|
||||
}
|
||||
|
||||
impl Jewel {
|
||||
pub fn into_persistent(&self) -> String {
|
||||
format!(
|
||||
"{}|{}|{}|{}",
|
||||
self.rarity, self.level, self.attribute, self.stat
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_persistent<'a>(split: &mut impl Iterator<Item = &'a str>) -> Result<Self> {
|
||||
let rarity = Rarities::from_str(split.next().unwrap())?;
|
||||
let level = split.next().unwrap().parse::<u32>()?;
|
||||
let attribute = Attribute::from_str(split.next().unwrap())?;
|
||||
let stat = StatisticType::from_str(split.next().unwrap())?;
|
||||
|
||||
Ok(Self {
|
||||
rarity,
|
||||
level,
|
||||
attribute,
|
||||
stat,
|
||||
|
||||
icon: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_stat(&mut self, item_settings: &ItemSettings) {
|
||||
self.stat
|
||||
.apply_for_jewel(self.rarity, self.level, item_settings);
|
||||
}
|
||||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
gui_handler: &Arc<GuiHandler>,
|
||||
item_settings: &ItemSettings,
|
||||
position: (i32, i32),
|
||||
) -> Result<Tooltip> {
|
||||
let inspector_snippet: Arc<GuiBuilder> = GuiBuilder::from_str(
|
||||
gui_handler,
|
||||
include_str!("../../resources/jewel_tooltip.xml"),
|
||||
)?;
|
||||
|
||||
let main_grid: Arc<Grid> = inspector_snippet.element("main_grid")?;
|
||||
main_grid.change_position_unscaled(position.0, position.1)?;
|
||||
|
||||
let jewel_icon: Arc<Icon> = inspector_snippet.element("jewel_icon")?;
|
||||
let rarity_label: Arc<Label> = inspector_snippet.element("rarity_label")?;
|
||||
|
||||
let attribute_type: Arc<Label> = inspector_snippet.element("attribute_type")?;
|
||||
let attribute_value: Arc<Label> = inspector_snippet.element("attribute_value")?;
|
||||
|
||||
let stat_type: Arc<Label> = inspector_snippet.element("stat_type")?;
|
||||
let stat_value: Arc<Label> = inspector_snippet.element("stat_value")?;
|
||||
|
||||
jewel_icon.set_icon(&self.icon())?;
|
||||
rarity_label.set_text(format!("{} ({})", self.rarity, self.level))?;
|
||||
|
||||
attribute_type.set_text({
|
||||
let mut s = self.attribute.to_string();
|
||||
s.replace_range(
|
||||
0..1,
|
||||
from_utf8(&[s.as_bytes()[0].to_ascii_uppercase()]).unwrap(),
|
||||
);
|
||||
|
||||
s
|
||||
})?;
|
||||
|
||||
attribute_value.set_text(
|
||||
item_settings
|
||||
.jewel_rarity_multiplier
|
||||
.from_rarity(self.rarity)
|
||||
* self.level
|
||||
* item_settings.general.jewel_level_multiplier,
|
||||
)?;
|
||||
|
||||
stat_type.set_text(&self.stat)?;
|
||||
stat_value.set_text(self.stat.display_value())?;
|
||||
|
||||
Ok(Tooltip::new(
|
||||
main_grid,
|
||||
inspector_snippet,
|
||||
gui_handler.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for Jewel {
|
||||
fn rarity(&self) -> Rarities {
|
||||
self.rarity
|
||||
}
|
||||
|
||||
fn icon(&self) -> Arc<Image> {
|
||||
self.icon.clone().unwrap()
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use crate::components::inventory::Storable;
|
||||
|
||||
use super::{ability_book::Ability, ItemSystem, Rarities};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MapItem {
|
||||
rarity: Rarities,
|
||||
|
||||
icon: Arc<Image>,
|
||||
}
|
||||
|
||||
impl MapItem {
|
||||
pub fn into_persistent(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
pub fn from_persistent<'a, A: Ability>(
|
||||
mut _split: impl Iterator<Item = &'a str>,
|
||||
_item_system: &ItemSystem<A>,
|
||||
) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn create_tooltip(
|
||||
&self,
|
||||
_gui_handler: &Arc<GuiHandler>,
|
||||
_position: (i32, i32),
|
||||
) -> Result<Arc<GuiBuilder>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for MapItem {
|
||||
fn rarity(&self) -> Rarities {
|
||||
self.rarity
|
||||
}
|
||||
|
||||
fn icon(&self) -> Arc<Image> {
|
||||
self.icon.clone()
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
pub mod ability_addon;
|
||||
pub mod ability_book;
|
||||
mod item;
|
||||
mod item_slots;
|
||||
mod item_system;
|
||||
mod jewel;
|
||||
mod map_item;
|
||||
mod rarities;
|
||||
mod tooltip;
|
||||
|
||||
pub use item::*;
|
||||
pub use item_slots::*;
|
||||
pub use item_system::{ItemSystem, Loot};
|
||||
pub use jewel::Jewel;
|
||||
pub use map_item::MapItem;
|
||||
pub use rarities::*;
|
||||
pub use tooltip::*;
|
|
@ -1,134 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use image::RgbaImage;
|
||||
|
||||
use std::{fmt, slice::Iter};
|
||||
|
||||
use crate::config::items::{ItemSettings, RarityColorSettings};
|
||||
|
||||
use super::{ability_book::Ability, ItemSystem};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Rarities {
|
||||
Common,
|
||||
Uncommon,
|
||||
Magical,
|
||||
Rare,
|
||||
Epic,
|
||||
Legendary,
|
||||
}
|
||||
|
||||
impl Rarities {
|
||||
const COUNT: u32 = 6;
|
||||
|
||||
pub fn iter() -> Iter<'static, Rarities> {
|
||||
use Rarities::*;
|
||||
|
||||
static RARITIES: [Rarities; Rarities::COUNT as usize] =
|
||||
[Common, Uncommon, Magical, Rare, Epic, Legendary];
|
||||
RARITIES.iter()
|
||||
}
|
||||
|
||||
pub fn random(item_settings: &ItemSettings, level: u32) -> Self {
|
||||
let p = Coin::raw();
|
||||
let multiplier = level as f32 / item_settings.general.drop_chance_reference_level as f32;
|
||||
|
||||
// legendary
|
||||
let mut lower = 0.0;
|
||||
let mut upper = item_settings.rarity_drop_rates.legendary * multiplier;
|
||||
|
||||
if Self::is_between(p, lower, upper) {
|
||||
return Self::Legendary;
|
||||
}
|
||||
|
||||
// epic
|
||||
lower = upper;
|
||||
upper = upper + item_settings.rarity_drop_rates.epic * multiplier;
|
||||
|
||||
if Self::is_between(p, lower, upper) {
|
||||
return Self::Epic;
|
||||
}
|
||||
|
||||
// rare
|
||||
lower = upper;
|
||||
upper = upper + item_settings.rarity_drop_rates.rare * multiplier;
|
||||
|
||||
if Self::is_between(p, lower, upper) {
|
||||
return Self::Rare;
|
||||
}
|
||||
|
||||
// magical
|
||||
lower = upper;
|
||||
upper = upper + item_settings.rarity_drop_rates.magical * multiplier;
|
||||
|
||||
if Self::is_between(p, lower, upper) {
|
||||
return Self::Magical;
|
||||
}
|
||||
|
||||
// uncommon
|
||||
lower = upper;
|
||||
upper = upper + item_settings.rarity_drop_rates.uncommon * multiplier;
|
||||
|
||||
if Self::is_between(p, lower, upper) {
|
||||
return Self::Uncommon;
|
||||
}
|
||||
|
||||
// common
|
||||
Self::Common
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_between(p: f32, l: f32, h: f32) -> bool {
|
||||
p >= l && p <= h
|
||||
}
|
||||
|
||||
pub(crate) fn apply_color<A: Ability>(
|
||||
&self,
|
||||
base_image: &RgbaImage,
|
||||
rarity_color_settings: &RarityColorSettings,
|
||||
) -> RgbaImage {
|
||||
ItemSystem::<A>::apply_color(base_image, rarity_color_settings.from_rarity(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Rarities {
|
||||
fn default() -> Self {
|
||||
Self::Common
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Rarities {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"Common" => Ok(Self::Common),
|
||||
"Uncommon" => Ok(Self::Uncommon),
|
||||
"Magical" => Ok(Self::Magical),
|
||||
"Rare" => Ok(Self::Rare),
|
||||
"Epic" => Ok(Self::Epic),
|
||||
"Legendary" => Ok(Self::Legendary),
|
||||
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Failed parsing Rarities from {}",
|
||||
s
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Rarities {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Common => write!(f, "Common"),
|
||||
Self::Uncommon => write!(f, "Uncommon"),
|
||||
Self::Magical => write!(f, "Magical"),
|
||||
Self::Rare => write!(f, "Rare"),
|
||||
Self::Epic => write!(f, "Epic"),
|
||||
Self::Legendary => write!(f, "Legendary"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
use anyhow::{bail, Result};
|
||||
use engine::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct FittingResult {
|
||||
pub fit: bool,
|
||||
pub start: i32,
|
||||
pub extent: u32,
|
||||
}
|
||||
|
||||
pub struct Tooltip {
|
||||
grid: Arc<Grid>,
|
||||
gui: Arc<GuiBuilder>,
|
||||
gui_handler: Arc<GuiHandler>,
|
||||
}
|
||||
|
||||
impl Tooltip {
|
||||
pub fn new(grid: Arc<Grid>, gui: Arc<GuiBuilder>, gui_handler: Arc<GuiHandler>) -> Self {
|
||||
Self {
|
||||
grid,
|
||||
gui,
|
||||
gui_handler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable(&self) -> Result<()> {
|
||||
self.gui.enable()
|
||||
}
|
||||
|
||||
pub fn check_fitting(&self) -> Result<(FittingResult, FittingResult)> {
|
||||
if !self.grid.visible() {
|
||||
bail!("enable item tooltip first");
|
||||
}
|
||||
|
||||
let (x, y, w, h) = self.grid.position_extent();
|
||||
|
||||
Ok((
|
||||
FittingResult {
|
||||
fit: (x + w as i32) <= self.gui_handler.width() as i32,
|
||||
start: x,
|
||||
extent: w,
|
||||
},
|
||||
FittingResult {
|
||||
fit: (y + h as i32) <= self.gui_handler.height() as i32,
|
||||
start: y,
|
||||
extent: h,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn move_to(&self, x: i32, y: i32) -> Result<()> {
|
||||
self.grid.change_position_unscaled(x, y)
|
||||
}
|
||||
|
||||
pub fn position_extent(&self) -> (i32, i32, u32, u32) {
|
||||
self.grid.position_extent()
|
||||
}
|
||||
|
||||
pub fn perform_single_check(&self, x: i32, y: i32) -> Result<()> {
|
||||
let (width_fitting, height_fitting) = self.check_fitting()?;
|
||||
|
||||
let mut width_shift = None;
|
||||
let mut height_shift = None;
|
||||
|
||||
let gui_pos = self.position_extent();
|
||||
|
||||
if !width_fitting.fit {
|
||||
width_shift = Some(x - gui_pos.2 as i32);
|
||||
}
|
||||
|
||||
if !height_fitting.fit {
|
||||
height_shift = Some(y - gui_pos.3 as i32);
|
||||
}
|
||||
|
||||
if width_shift.is_some() || height_shift.is_some() {
|
||||
self.move_to(
|
||||
width_shift.unwrap_or(gui_pos.0),
|
||||
height_shift.unwrap_or(gui_pos.1),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn perform_double_check(&self, other: &Self, x: i32, spacing: u32) -> Result<()> {
|
||||
let (_left_width_fitting, left_height_fitting) = self.check_fitting()?;
|
||||
let (right_width_fitting, right_height_fitting) = other.check_fitting()?;
|
||||
|
||||
let mut width_shift = None;
|
||||
let mut height_shift = None;
|
||||
|
||||
let left_gui_pos = self.position_extent();
|
||||
let right_gui_pos = other.position_extent();
|
||||
|
||||
if !right_width_fitting.fit {
|
||||
width_shift = Some(x - right_gui_pos.2 as i32 - spacing as i32);
|
||||
}
|
||||
|
||||
let window_height = self.gui_handler.height();
|
||||
|
||||
if !left_height_fitting.fit {
|
||||
height_shift = Some(window_height as i32 - left_gui_pos.3 as i32 - spacing as i32);
|
||||
}
|
||||
|
||||
if !right_height_fitting.fit {
|
||||
let right = window_height as i32 - right_gui_pos.3 as i32 - spacing as i32;
|
||||
|
||||
match height_shift {
|
||||
Some(current) => {
|
||||
if current > right {
|
||||
height_shift = Some(right);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
height_shift = Some(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if width_shift.is_some() || height_shift.is_some() {
|
||||
let width = width_shift.unwrap_or(right_gui_pos.0);
|
||||
let height = height_shift.unwrap_or(right_gui_pos.1);
|
||||
|
||||
other.move_to(width, height)?;
|
||||
self.move_to(width - spacing as i32 - left_gui_pos.2 as i32, height)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Arc<GuiBuilder>> for Tooltip {
|
||||
fn into(self) -> Arc<GuiBuilder> {
|
||||
self.gui
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
pub mod components;
|
||||
pub mod config;
|
||||
pub mod damage_type;
|
||||
pub mod items;
|
Loading…
Reference in a new issue