Compare commits
7 Commits
a86b57d85c
...
92a03ccbd1
Author | SHA1 | Date |
---|---|---|
Mark | 92a03ccbd1 | |
Mark | 69b35bd43c | |
Mark | 327e2b43b3 | |
Mark | 5528f6ed4e | |
Mark | 308e380241 | |
Mark | 855eb0680b | |
Mark | f53e191de3 |
5
TODO.md
5
TODO.md
|
@ -1,8 +1,11 @@
|
|||
# Specific projects
|
||||
|
||||
## Now:
|
||||
- init on resize
|
||||
- hide ui element
|
||||
- zoom limits
|
||||
- text scrolling
|
||||
- scrollbox scroll limits
|
||||
- clean up content
|
||||
- Clean up state api
|
||||
- Clean up & document UI api
|
||||
|
@ -12,6 +15,8 @@
|
|||
- Selection while flying
|
||||
- outfitter
|
||||
- fps textbox positioning
|
||||
- shield generation curve
|
||||
- clippy & rules
|
||||
|
||||
## Small jobs
|
||||
- Better planet icons in radar
|
||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit b8ea8c5a04c0216304a81ebf396b7320a709d6ed
|
||||
Subproject commit bd425ccb10bb16661620916ff4f1a387b54ce7ca
|
|
@ -2,6 +2,14 @@
|
|||
name = "Plasma Engines"
|
||||
thumbnail = "icon::engine"
|
||||
|
||||
desc = """
|
||||
This is the smallest of the plasma propulsion systems produced by
|
||||
Delta V Corporation, suitable for very light fighters and interceptors.
|
||||
|
||||
Plasma engines are a bit more powerful than ion engines of the same size,
|
||||
but they are less energy efficient and produce more heat.
|
||||
"""
|
||||
|
||||
space.engine = 20
|
||||
engine.thrust = 100
|
||||
engine.flare.sprite = "flare::ion"
|
||||
|
@ -14,6 +22,15 @@ steering.power = 20
|
|||
thumbnail = "icon::shield"
|
||||
name = "Shield Generator"
|
||||
|
||||
desc = """
|
||||
This is the standard shield generator for fighters and interceptors,
|
||||
as well as for many non-combat starships. Although it is possible for
|
||||
a ship to have no shield generator at all, recharging its shield matrix
|
||||
only when landed in a hospitable spaceport, life in deep space is
|
||||
unpredictable enough that most pilots find shield generators to be
|
||||
well worth the space they take up.
|
||||
"""
|
||||
|
||||
space.outfit = 5
|
||||
shield.generation = 10
|
||||
shield.strength = 500
|
||||
|
@ -24,6 +41,13 @@ shield.delay = 2.0
|
|||
thumbnail = "icon::blaster"
|
||||
name = "Blaster"
|
||||
|
||||
desc = """
|
||||
Although not the most accurate or damaging of weapons, the Energy Blaster is popular because it
|
||||
is small enough to be installed on even the tiniest of ships. One blaster is not enough to do
|
||||
appreciable damage to anything larger than a fighter, but a ship equipped with several of them
|
||||
becomes a force to be reckoned with.
|
||||
"""
|
||||
|
||||
space.weapon = 10
|
||||
|
||||
# Average delay between shots
|
||||
|
|
|
@ -57,8 +57,8 @@ fn init(state) {
|
|||
(radar_size / 2.0 + 5),
|
||||
(radar_size / -2.0 - 5),
|
||||
3.5, 3.5,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
);
|
||||
|
||||
sprite::add("radar.frame.ne", "ui::radarframe", init_pos);
|
||||
|
@ -114,12 +114,12 @@ fn event(state, event) {
|
|||
|
||||
fn step(state) {
|
||||
radialbar::set_val("shield",
|
||||
state.player_ship().get_shields()
|
||||
/ state.player_ship().get_total_shields()
|
||||
state.player_ship().current_shields()
|
||||
/ state.player_ship().stat_shield_strength()
|
||||
);
|
||||
radialbar::set_val("hull",
|
||||
state.player_ship().get_hull()
|
||||
/ state.player_ship().get_total_hull()
|
||||
state.player_ship().current_hull()
|
||||
/ state.player_ship().total_hull()
|
||||
);
|
||||
|
||||
|
||||
|
@ -138,8 +138,8 @@ fn step(state) {
|
|||
Rect(
|
||||
pos.x(), pos.y(),
|
||||
5.0, 5.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::set_angle("radar.arrow", angle - 90.0);
|
||||
|
@ -201,8 +201,8 @@ fn step(state) {
|
|||
Rect(
|
||||
pos.x(), pos.y(),
|
||||
size, size,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
@ -211,8 +211,8 @@ fn step(state) {
|
|||
Rect(
|
||||
pos.x(), pos.y(),
|
||||
size, size,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -263,8 +263,8 @@ fn step(state) {
|
|||
Rect(
|
||||
pos.x(), pos.y(),
|
||||
size, size,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
@ -273,8 +273,8 @@ fn step(state) {
|
|||
Rect(
|
||||
pos.x(), pos.y(),
|
||||
size, size,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -291,8 +291,8 @@ fn step(state) {
|
|||
(radar_size / 2.0 + 5) - dx,
|
||||
(radar_size / -2.0 - 5) + dy,
|
||||
3.5, 3.5,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::set_rect("radar.frame.se",
|
||||
|
@ -300,8 +300,8 @@ fn step(state) {
|
|||
(radar_size / 2.0 + 5) - dx,
|
||||
(radar_size / -2.0 - 5) - dy,
|
||||
3.5, 3.5,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::set_rect("radar.frame.sw",
|
||||
|
@ -309,8 +309,8 @@ fn step(state) {
|
|||
(radar_size / 2.0 + 5) + dx,
|
||||
(radar_size / -2.0 - 5) - dy,
|
||||
3.5, 3.5,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::set_rect("radar.frame.nw",
|
||||
|
@ -318,8 +318,8 @@ fn step(state) {
|
|||
(radar_size / 2.0 + 5) + dx,
|
||||
(radar_size / -2.0 - 5) + dy,
|
||||
3.5, 3.5,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ fn init(state) {
|
|||
"ui::planet::button",
|
||||
Rect(
|
||||
99.0, 128.0, 73.898, 18.708,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -23,8 +23,8 @@ fn init(state) {
|
|||
},
|
||||
Rect(
|
||||
-180.0, 142.0, 274.0, 135.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
sprite::set_mask("landscape", "ui::landscapemask");
|
||||
|
@ -40,17 +40,17 @@ fn init(state) {
|
|||
);
|
||||
// If this is not set, the button will
|
||||
// not receive events
|
||||
sprite::set_disable_events("frame", true);
|
||||
sprite::disable_events("frame", true);
|
||||
|
||||
|
||||
textbox::add(
|
||||
"title", 10.0, 10.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-70.79, 138.0, 59.867, 10.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::align_center("title");
|
||||
textbox::font_serif("title");
|
||||
|
@ -61,12 +61,12 @@ fn init(state) {
|
|||
|
||||
textbox::add(
|
||||
"desc", 7.5, 8.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-178.92, -20.3, 343.0, 81.467,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_sans("desc");
|
||||
if state.player_ship().is_landed() {
|
||||
|
|
|
@ -3,6 +3,11 @@ fn init(state) {
|
|||
conf::show_starfield(true);
|
||||
conf::show_phys(false);
|
||||
|
||||
if !state.player_ship().is_landed() {
|
||||
print("UI error: player isn't landed when initializing outfitter scene");
|
||||
return;
|
||||
}
|
||||
|
||||
sprite::add(
|
||||
"se_box",
|
||||
"ui::outfitterbox",
|
||||
|
@ -15,12 +20,13 @@ fn init(state) {
|
|||
|
||||
textbox::add(
|
||||
"exit_text", 10.0, 10.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
122.71, 48.0, 51.0, 12.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::SouthWest
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::SouthWest,
|
||||
Anchor::NorthWest
|
||||
|
||||
)
|
||||
);
|
||||
textbox::font_serif("exit_text");
|
||||
textbox::align_center("exit_text");
|
||||
|
@ -31,8 +37,8 @@ fn init(state) {
|
|||
"ui::button",
|
||||
Rect(
|
||||
113.35, 52.0, 69.8, 18.924,
|
||||
Anchor::NorthWest,
|
||||
Anchor::SouthWest
|
||||
Anchor::SouthWest,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -53,53 +59,46 @@ fn init(state) {
|
|||
"icon::gypsum",
|
||||
Rect(
|
||||
111.0, -95.45, 90.0, 90.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::preserve_aspect("ship_thumb", true);
|
||||
|
||||
textbox::add(
|
||||
"ship_name", 10.0, 10.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
111.0, -167.27, 145.0, 10.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
textbox::font_serif("ship_name");
|
||||
textbox::align_center("ship_name");
|
||||
textbox::set_text("ship_name", "Hyperion");
|
||||
|
||||
textbox::add(
|
||||
"ship_type", 7.0, 8.5,
|
||||
Color(0.7, 0.7, 0.7, 1.0),
|
||||
Rect(
|
||||
111.0, -178.0, 145.0, 8.5,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
),
|
||||
Color(0.7, 0.7, 0.7, 1.0)
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
textbox::font_sans("ship_type");
|
||||
textbox::align_center("ship_type");
|
||||
if state.player_ship().is_some() {
|
||||
textbox::set_text("ship_type", state.player_ship().display_name());
|
||||
}
|
||||
|
||||
|
||||
textbox::add(
|
||||
"ship_stats", 7.0, 8.5,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
38.526, -192.332, 144.948, 154.5,
|
||||
Anchor::NorthWest,
|
||||
Anchor::NorthWest,
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
)
|
||||
);
|
||||
textbox::font_mono("ship_stats");
|
||||
textbox::set_text("ship_stats", "Earth");
|
||||
|
||||
|
||||
sprite::add(
|
||||
"outfit_bg",
|
||||
|
@ -116,8 +115,8 @@ fn init(state) {
|
|||
"icon::engine",
|
||||
Rect(
|
||||
-166.0, -109.0, 90.0, 90.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthEast
|
||||
Anchor::NorthEast,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::preserve_aspect("outfit_thumb", true);
|
||||
|
@ -125,139 +124,258 @@ fn init(state) {
|
|||
|
||||
textbox::add(
|
||||
"outfit_name", 16.0, 16.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-312.0, -20.0, 200.0, 16.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::NorthEast,
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_serif("outfit_name");
|
||||
textbox::weight_bold("outfit_name");
|
||||
textbox::set_text("outfit_name", "Earth");
|
||||
textbox::set_text("outfit_name", "");
|
||||
|
||||
textbox::add(
|
||||
"outfit_desc", 7.0, 8.5,
|
||||
"outfit_desc", 8.5, 9.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-166.0, -219.0, 260.0, 78.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthEast,
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
textbox::font_serif("outfit_desc");
|
||||
textbox::set_text("outfit_desc", "Earth");
|
||||
textbox::set_text("outfit_desc", "");
|
||||
|
||||
|
||||
textbox::add(
|
||||
"outfit_stats", 7.0, 8.5,
|
||||
"outfit_stats", 8.5, 9.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-295.0, -271.0, 164.0, 216.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::NorthEast,
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_mono("outfit_stats");
|
||||
textbox::set_text("outfit_stats", "Earth");
|
||||
textbox::set_text("outfit_stats", "");
|
||||
|
||||
|
||||
// width should be calculated as a fraction of screen width
|
||||
let scrollbox_rect = Rect(
|
||||
222.0, -16.0, 470.0, 480.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::NorthWest,
|
||||
|
||||
sprite::add(
|
||||
"buy_button",
|
||||
"ui::button",
|
||||
Rect(
|
||||
-110.71, -281.0, 69.8, 18.924,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
|
||||
scrollbox::add("outfit_list", scrollbox_rect);
|
||||
textbox::add(
|
||||
"buy_text", 10.0, 10.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-100.84, -285.34, 51.0, 12.0,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
|
||||
)
|
||||
);
|
||||
textbox::font_serif("buy_text");
|
||||
textbox::align_center("buy_text");
|
||||
textbox::set_text("buy_text", "Buy");
|
||||
|
||||
sprite::add(
|
||||
"sell_button",
|
||||
"ui::button",
|
||||
Rect(
|
||||
-110.71, -306.2, 69.8, 18.924,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
|
||||
textbox::add(
|
||||
"sell_text", 10.0, 10.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
-100.84, -311.15, 51.0, 12.0,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_serif("sell_text");
|
||||
textbox::align_center("sell_text");
|
||||
textbox::set_text("sell_text", "Sell");
|
||||
|
||||
|
||||
|
||||
let times_five = false;
|
||||
let times_ten = false;
|
||||
let times_hundred = false;
|
||||
|
||||
textbox::add(
|
||||
"five_text", 7.5, 7.5,
|
||||
Color(0.5, 0.5, 0.5, 1.0),
|
||||
Rect(
|
||||
-110.71, -331.2, 69.8, 18.924,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_mono("five_text");
|
||||
textbox::set_text("five_text", "[shift] x5");
|
||||
|
||||
textbox::add(
|
||||
"ten_text", 7.5, 7.5,
|
||||
Color(0.5, 0.5, 0.5, 1.0),
|
||||
Rect(
|
||||
-110.71, -341.2, 69.8, 18.924,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_mono("ten_text");
|
||||
textbox::set_text("ten_text", "[ctrl] x10");
|
||||
|
||||
|
||||
textbox::add(
|
||||
"hundred_text", 7.5, 7.5,
|
||||
Color(0.5, 0.5, 0.5, 1.0),
|
||||
Rect(
|
||||
-110.71, -351.2, 69.8, 18.924,
|
||||
Anchor::NorthEast,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
textbox::font_mono("hundred_text");
|
||||
textbox::set_text("hundred_text", "[alt] x100");
|
||||
|
||||
|
||||
|
||||
|
||||
// A string containing the selected outfit's index.
|
||||
// This is always set---if we have an outfitter, we must have at least one outfit.
|
||||
let selected_outfit = false;
|
||||
|
||||
{
|
||||
|
||||
let scrollbox_width = state.window_size().x() - (190 + 16 + 300 + 16);
|
||||
if scrollbox_width <= 0 {
|
||||
scrollbox_width = 1;
|
||||
}
|
||||
|
||||
// width should be calculated as a fraction of screen width
|
||||
let scrollbox_rect = Rect(
|
||||
222.0, -16.0, scrollbox_width, 480.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::NorthWest
|
||||
);
|
||||
|
||||
scrollbox::add("outfit_list", scrollbox_rect);
|
||||
|
||||
|
||||
// p cannot be saved in the global scope.
|
||||
let p = state.player_ship();
|
||||
if p.is_landed() {
|
||||
let s = "";
|
||||
let x = scrollbox_rect.pos().x() + 45.0;
|
||||
let y = scrollbox_rect.pos().y() - 45.0;
|
||||
for xxx in ["1","2","3"] {
|
||||
for i in p.landed_on().outfitter() {
|
||||
s = s + i.display_name() + "\n";
|
||||
let s = "";
|
||||
let x = scrollbox_rect.pos().x() + 45.0;
|
||||
let y = scrollbox_rect.pos().y() - 45.0;
|
||||
for i in p.landed_on().outfitter() {
|
||||
|
||||
let thumb_name = "outfit.thumb." + i.index() + xxx;
|
||||
let backg_name = "outfit.backg." + i.index() + xxx;
|
||||
let title_name = "outfit.title." + i.index() + xxx;
|
||||
|
||||
sprite::add(
|
||||
backg_name,
|
||||
"ui::outfitbg",
|
||||
Rect(
|
||||
x, y, 90.0, 90.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
sprite::preserve_aspect(backg_name, true);
|
||||
scrollbox::add_element("outfit_list", backg_name);
|
||||
|
||||
sprite::add(
|
||||
thumb_name,
|
||||
i.thumbnail(),
|
||||
Rect(
|
||||
x, y, 75.0, 75.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest
|
||||
)
|
||||
);
|
||||
sprite::preserve_aspect(thumb_name, true);
|
||||
scrollbox::add_element("outfit_list", thumb_name);
|
||||
|
||||
textbox::add(
|
||||
title_name,
|
||||
10.0, 10.0,
|
||||
Rect(
|
||||
x, y - 50.0, 90.0, 10.0,
|
||||
Anchor::Center,
|
||||
Anchor::NorthWest,
|
||||
),
|
||||
Color(1.0, 1.0, 1.0, 1.0)
|
||||
);
|
||||
textbox::font_sans(title_name);
|
||||
textbox::align_center(title_name);
|
||||
textbox::set_text(title_name, i.display_name());
|
||||
scrollbox::add_element("outfit_list", title_name);
|
||||
|
||||
x = x + 120.0;
|
||||
if x > (
|
||||
scrollbox_rect.pos().x() + scrollbox_rect.dim().x() - 45.0
|
||||
) {
|
||||
x = scrollbox_rect.pos().x() + 45.0;
|
||||
y = y - 120.0;
|
||||
}
|
||||
if selected_outfit == false {
|
||||
selected_outfit = i.index();
|
||||
update_outfit_info(selected_outfit);
|
||||
}
|
||||
|
||||
s = s + i.display_name() + "\n";
|
||||
|
||||
let thumb_name = "outfit.thumb." + i.index();
|
||||
let backg_name = "outfit.backg." + i.index();
|
||||
let title_name = "outfit.title." + i.index();
|
||||
|
||||
sprite::add(
|
||||
backg_name,
|
||||
"ui::outfitbg",
|
||||
Rect(
|
||||
x, y, 90.0, 90.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::preserve_aspect(backg_name, true);
|
||||
scrollbox::add_element("outfit_list", backg_name);
|
||||
|
||||
sprite::add(
|
||||
thumb_name,
|
||||
i.thumbnail(),
|
||||
Rect(
|
||||
x, y, 75.0, 75.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
sprite::preserve_aspect(thumb_name, true);
|
||||
scrollbox::add_element("outfit_list", thumb_name);
|
||||
|
||||
textbox::add(
|
||||
title_name,
|
||||
10.0, 10.0,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Rect(
|
||||
x, y - 50.0, 90.0, 10.0,
|
||||
Anchor::NorthWest,
|
||||
Anchor::Center
|
||||
)
|
||||
);
|
||||
textbox::font_sans(title_name);
|
||||
textbox::align_center(title_name);
|
||||
textbox::set_text(title_name, i.display_name());
|
||||
scrollbox::add_element("outfit_list", title_name);
|
||||
|
||||
x = x + 120.0;
|
||||
if x > (
|
||||
scrollbox_rect.pos().x() + scrollbox_rect.dim().x() - 45.0
|
||||
) {
|
||||
x = scrollbox_rect.pos().x() + 45.0;
|
||||
y = y - 120.0;
|
||||
}
|
||||
textbox::set_text("outfit_stats", s);
|
||||
}
|
||||
}
|
||||
|
||||
update_ship_info(state);
|
||||
}
|
||||
|
||||
|
||||
fn event(state, event) {
|
||||
if type_of(event) == "MouseHoverEvent" {
|
||||
let element = event.element();
|
||||
if element == "exit_button" {
|
||||
|
||||
if (
|
||||
element == "exit_button"
|
||||
|| element == "buy_button"
|
||||
|| element == "sell_button"
|
||||
){
|
||||
if event.is_enter() {
|
||||
sprite::jump_to("exit_button", "on:top", 0.1);
|
||||
sprite::jump_to(element, "on:top", 0.1);
|
||||
} else {
|
||||
sprite::jump_to("exit_button", "off:top", 0.1);
|
||||
sprite::jump_to(element, "off:top", 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
if element.starts_with("outfit.backg.") && element != selected_outfit {
|
||||
if event.is_enter() {
|
||||
sprite::jump_to(element, "hover:top", 0.1);
|
||||
|
||||
if element.starts_with("outfit.backg.") {
|
||||
if event.element().split("outfit.backg.".len())[1] == selected_outfit {
|
||||
if event.is_enter() {
|
||||
sprite::jump_to(element, "hoverselected:top", 0.1);
|
||||
} else {
|
||||
sprite::jump_to(element, "selected:top", 0.1);
|
||||
}
|
||||
} else {
|
||||
sprite::jump_to(element, "off:top", 0.1);
|
||||
if event.is_enter() {
|
||||
sprite::jump_to(element, "hover:top", 0.1);
|
||||
} else {
|
||||
sprite::jump_to(element, "off:top", 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,15 +383,10 @@ fn event(state, event) {
|
|||
}
|
||||
|
||||
|
||||
// TODO: this occasionally breaks because of sprite ordering.
|
||||
// Clicks go to se_box instead!
|
||||
if type_of(event) == "MouseClickEvent" {
|
||||
if !event.is_down() {
|
||||
return PlayerDirective::None;
|
||||
}
|
||||
|
||||
print(event.element());
|
||||
|
||||
|
||||
let element = event.element();
|
||||
if element == "exit_button" {
|
||||
|
@ -281,22 +394,183 @@ fn event(state, event) {
|
|||
return PlayerDirective::None;
|
||||
}
|
||||
|
||||
if element.starts_with("outfit.backg.") && element != selected_outfit {
|
||||
if (
|
||||
element.starts_with("outfit.backg.") &&
|
||||
event.element().split("outfit.backg.".len())[1] != selected_outfit
|
||||
) {
|
||||
if selected_outfit != false {
|
||||
sprite::jump_to(selected_outfit, "off:top", 0.1);
|
||||
sprite::jump_to("outfit.backg." + selected_outfit, "off:top", 0.1);
|
||||
}
|
||||
sprite::jump_to(element, "selected:top", 0.1);
|
||||
selected_outfit = element;
|
||||
/// We can't click on this sprite without first hovering!
|
||||
sprite::jump_to(element, "hoverselected:top", 0.1);
|
||||
selected_outfit = event.element().split("outfit.backg.".len())[1];
|
||||
update_outfit_info(selected_outfit);
|
||||
|
||||
return PlayerDirective::None;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if type_of(event) == "KeyboardEvent" {
|
||||
if event.key() == "up" {
|
||||
times_five = event.is_down();
|
||||
}
|
||||
if event.key() == "left" {
|
||||
times_ten = event.is_down();
|
||||
}
|
||||
if event.key() == "right" {
|
||||
times_hundred = event.is_down();
|
||||
}
|
||||
|
||||
update_outfit_box(times_five, times_ten, times_hundred);
|
||||
return PlayerDirective::None;
|
||||
}
|
||||
|
||||
if type_of(event) == "PlayerShipStateEvent" {
|
||||
if !state.player_ship().is_landed() {
|
||||
ui::go_to_scene("flying");
|
||||
}
|
||||
return PlayerDirective::None;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_outfit_box(times_five, times_ten, times_hundred) {
|
||||
let times = 1;
|
||||
|
||||
if times_five {
|
||||
times *= 5;
|
||||
textbox::set_color("five_text", Color(1.0, 1.0, 1.0, 1.0));
|
||||
} else {
|
||||
textbox::set_color("five_text", Color(0.5, 0.5, 0.5, 1.0));
|
||||
}
|
||||
|
||||
if times_ten {
|
||||
times *= 10;
|
||||
textbox::set_color("ten_text", Color(1.0, 1.0, 1.0, 1.0));
|
||||
} else {
|
||||
textbox::set_color("ten_text", Color(0.5, 0.5, 0.5, 1.0));
|
||||
}
|
||||
|
||||
if times_hundred {
|
||||
times *= 100;
|
||||
textbox::set_color("hundred_text", Color(1.0, 1.0, 1.0, 1.0));
|
||||
} else {
|
||||
textbox::set_color("hundred_text", Color(0.5, 0.5, 0.5, 1.0));
|
||||
}
|
||||
|
||||
if times != 1 {
|
||||
textbox::set_text("buy_text", "Buy x" + times);
|
||||
textbox::set_text("sell_text", "Sell x" + times);
|
||||
} else {
|
||||
textbox::set_text("buy_text", "Buy");
|
||||
textbox::set_text("sell_text", "Sell");
|
||||
}
|
||||
}
|
||||
|
||||
fn update_outfit_info(selected_outfit) {
|
||||
let outfit = ct::get_outfit(selected_outfit);
|
||||
if outfit.is_some() {
|
||||
|
||||
let stats = "";
|
||||
let tlen = 20;
|
||||
if outfit.stat_thrust() != 0 {
|
||||
let s = "thrust ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + outfit.stat_thrust();
|
||||
stats += "\n";
|
||||
}
|
||||
if outfit.stat_steer_power() != 0 {
|
||||
let s = "steer power ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + outfit.stat_steer_power();
|
||||
stats += "\n";
|
||||
}
|
||||
if outfit.stat_shield_strength() != 0 {
|
||||
let s = "shield strength ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + outfit.stat_shield_strength();
|
||||
stats += "\n";
|
||||
}
|
||||
if outfit.stat_shield_generation() != 0 {
|
||||
let s = "shield regen";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + outfit.stat_shield_generation();
|
||||
stats += "\n";
|
||||
}
|
||||
if outfit.stat_shield_delay() != 0 {
|
||||
let s = "shield delay ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + outfit.stat_shield_delay();
|
||||
stats += "\n";
|
||||
}
|
||||
if outfit.stat_shield_dps() != 0 {
|
||||
let s = "shield dps ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + outfit.stat_shield_dps();
|
||||
stats += "\n";
|
||||
}
|
||||
|
||||
sprite::set_sprite("outfit_thumb", outfit.thumbnail());
|
||||
textbox::set_text("outfit_name", outfit.display_name());
|
||||
textbox::set_text("outfit_desc", outfit.desc());
|
||||
textbox::set_text("outfit_stats", stats);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ship_info(state) {
|
||||
let ship = state.player_ship();
|
||||
if ship.is_some() {
|
||||
|
||||
let stats = "";
|
||||
let tlen = 20;
|
||||
|
||||
|
||||
// TODO: outfits add mass
|
||||
// TODO: calculate radial acceleration
|
||||
{
|
||||
let s = "shield strength ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + ship.stat_shield_strength();
|
||||
stats += "\n";
|
||||
}
|
||||
{
|
||||
let s = "hull strength ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + ship.total_hull();
|
||||
stats += "\n\n";
|
||||
}
|
||||
{
|
||||
let s = "mass ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + ship.empty_mass();
|
||||
stats += "\n";
|
||||
}
|
||||
{
|
||||
let s = "thrust ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + ship.stat_thrust();
|
||||
stats += "\n";
|
||||
}
|
||||
{
|
||||
let s = "steer power ";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + ship.stat_steer_power();
|
||||
stats += "\n";
|
||||
}
|
||||
|
||||
{
|
||||
let s = "max shield regen";
|
||||
s.pad(tlen, " ");
|
||||
stats += s + ship.stat_max_shield_generation();
|
||||
stats += "\n";
|
||||
}
|
||||
|
||||
sprite::set_sprite("ship_thumb", ship.thumbnail());
|
||||
textbox::set_text("ship_name", "Hyperion");
|
||||
textbox::set_text("ship_type", state.player_ship().display_name());
|
||||
textbox::set_text("ship_stats", stats);
|
||||
|
||||
}
|
||||
}
|
|
@ -137,7 +137,7 @@ impl From<ImmutableString> for ContentIndex {
|
|||
|
||||
impl From<ContentIndex> for ImmutableString {
|
||||
fn from(value: ContentIndex) -> Self {
|
||||
ImmutableString::from(Arc::into_inner(value.0).unwrap())
|
||||
ImmutableString::from(value.0.as_ref().clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ pub(crate) mod system;
|
|||
pub use config::Config;
|
||||
pub use effect::*;
|
||||
pub use faction::{Faction, Relationship};
|
||||
pub use outfit::{Gun, Outfit, Projectile, ProjectileCollider};
|
||||
pub use outfit::*;
|
||||
pub use outfitspace::OutfitSpace;
|
||||
pub use ship::{
|
||||
CollapseEffectSpawner, CollapseEvent, EffectCollapseEvent, EnginePoint, GunPoint, Ship,
|
||||
|
|
|
@ -23,6 +23,7 @@ pub(crate) mod syntax {
|
|||
pub struct Outfit {
|
||||
pub thumbnail: ContentIndex,
|
||||
pub name: String,
|
||||
pub desc: String,
|
||||
pub engine: Option<Engine>,
|
||||
pub steering: Option<Steering>,
|
||||
pub space: outfitspace::syntax::OutfitSpace,
|
||||
|
@ -145,15 +146,12 @@ pub struct Outfit {
|
|||
/// The name of this outfit
|
||||
pub display_name: String,
|
||||
|
||||
/// The description of this outfit
|
||||
pub desc: String,
|
||||
|
||||
/// Thie outfit's index
|
||||
pub index: ContentIndex,
|
||||
|
||||
/// How much engine thrust this outfit produces
|
||||
pub engine_thrust: f32,
|
||||
|
||||
/// How much steering power this outfit provids
|
||||
pub steer_power: f32,
|
||||
|
||||
/// The engine flare sprite this outfit creates.
|
||||
/// Its location and size is determined by a ship's
|
||||
/// engine points.
|
||||
|
@ -165,6 +163,23 @@ pub struct Outfit {
|
|||
/// Jump to this edge when engines turn off
|
||||
pub engine_flare_on_stop: Option<SectionEdge>,
|
||||
|
||||
/// This outfit's gun stats.
|
||||
/// If this is some, this outfit requires a gun point.
|
||||
pub gun: Option<Gun>,
|
||||
|
||||
/// The stats this outfit provides
|
||||
pub stats: OutfitStats,
|
||||
}
|
||||
|
||||
/// Outfit statistics
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutfitStats {
|
||||
/// How much engine thrust this outfit produces
|
||||
pub engine_thrust: f32,
|
||||
|
||||
/// How much steering power this outfit provids
|
||||
pub steer_power: f32,
|
||||
|
||||
/// Shield hit points
|
||||
pub shield_strength: f32,
|
||||
|
||||
|
@ -173,10 +188,37 @@ pub struct Outfit {
|
|||
|
||||
/// Wait this many seconds after taking damage before regenerating shields
|
||||
pub shield_delay: f32,
|
||||
}
|
||||
|
||||
/// This outfit's gun stats.
|
||||
/// If this is some, this outfit requires a gun point.
|
||||
pub gun: Option<Gun>,
|
||||
impl OutfitStats {
|
||||
/// Create a new `OutfitStats`, with all values set to zero.
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
engine_thrust: 0.0,
|
||||
steer_power: 0.0,
|
||||
shield_strength: 0.0,
|
||||
shield_generation: 0.0,
|
||||
shield_delay: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add all the stats in `other` to the stats in `self`.
|
||||
/// Sheld delay is not affected.
|
||||
pub fn add(&mut self, other: &Self) {
|
||||
self.engine_thrust += other.engine_thrust;
|
||||
self.steer_power += other.steer_power;
|
||||
self.shield_strength += other.shield_strength;
|
||||
self.shield_generation += other.shield_generation;
|
||||
}
|
||||
|
||||
/// Subtract all the stats in `other` from the stats in `self`.
|
||||
/// Sheld delay is not affected.
|
||||
pub fn subtract(&mut self, other: &Self) {
|
||||
self.engine_thrust -= other.engine_thrust;
|
||||
self.steer_power -= other.steer_power;
|
||||
self.shield_strength -= other.shield_strength;
|
||||
self.shield_generation -= other.shield_generation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines a projectile's collider
|
||||
|
@ -284,15 +326,12 @@ impl crate::Build for Outfit {
|
|||
display_name: outfit.name,
|
||||
thumbnail,
|
||||
gun,
|
||||
engine_thrust: 0.0,
|
||||
steer_power: 0.0,
|
||||
desc: outfit.desc,
|
||||
engine_flare_sprite: None,
|
||||
engine_flare_on_start: None,
|
||||
engine_flare_on_stop: None,
|
||||
space: OutfitSpace::from(outfit.space),
|
||||
shield_delay: 0.0,
|
||||
shield_generation: 0.0,
|
||||
shield_strength: 0.0,
|
||||
stats: OutfitStats::zero(),
|
||||
};
|
||||
|
||||
// Engine stats
|
||||
|
@ -307,7 +346,7 @@ impl crate::Build for Outfit {
|
|||
}
|
||||
Some(t) => t.clone(),
|
||||
};
|
||||
o.engine_thrust = engine.thrust;
|
||||
o.stats.engine_thrust = engine.thrust;
|
||||
o.engine_flare_sprite = Some(sprite.clone());
|
||||
|
||||
// Flare animation will traverse this edge when the player presses the thrust key
|
||||
|
@ -381,14 +420,14 @@ impl crate::Build for Outfit {
|
|||
|
||||
// Steering stats
|
||||
if let Some(steer) = outfit.steering {
|
||||
o.steer_power = steer.power;
|
||||
o.stats.steer_power = steer.power;
|
||||
}
|
||||
|
||||
// Shield stats
|
||||
if let Some(shield) = outfit.shield {
|
||||
o.shield_delay = shield.delay.unwrap_or(0.0);
|
||||
o.shield_generation = shield.generation.unwrap_or(0.0);
|
||||
o.shield_strength = shield.strength.unwrap_or(0.0);
|
||||
o.stats.shield_delay = shield.delay.unwrap_or(0.0);
|
||||
o.stats.shield_generation = shield.generation.unwrap_or(0.0);
|
||||
o.stats.shield_strength = shield.strength.unwrap_or(0.0);
|
||||
}
|
||||
|
||||
content.outfits.insert(o.index.clone(), Arc::new(o));
|
||||
|
|
|
@ -145,6 +145,7 @@ pub struct LandableSystemObject {
|
|||
pub image: Arc<Sprite>,
|
||||
|
||||
/// The outfits we can buy here
|
||||
/// If this is empty, this landable has no outfitter.
|
||||
pub outfitter: Vec<Arc<Outfit>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::globaluniform::{AtlasArray, AtlasImageLocation};
|
|||
use anyhow::Result;
|
||||
use bytemuck::Zeroable;
|
||||
use galactica_content::Content;
|
||||
use galactica_packer::SpriteAtlasImage;
|
||||
use galactica_util::constants::ASSET_CACHE;
|
||||
use image::GenericImageView;
|
||||
use log::info;
|
||||
|
@ -72,16 +71,6 @@ impl RawTexture {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Texture {
|
||||
pub index: u32, // Index in texture array
|
||||
pub len: u32, // Number of frames
|
||||
pub frame_duration: f32, // Frames per second
|
||||
pub aspect: f32, // width / height
|
||||
pub repeat: u32, // How to re-play this texture
|
||||
pub location: Vec<SpriteAtlasImage>,
|
||||
}
|
||||
|
||||
pub struct TextureArray {
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
pub bind_group_layout: BindGroupLayout,
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
use galactica_content::{Content, Outfit};
|
||||
use log::error;
|
||||
use rhai::{CustomType, FnNamespace, FuncRegistration, ImmutableString, Module, TypeBuilder};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn build_ct_module(ct_src: Arc<Content>) -> Module {
|
||||
let mut module = Module::new();
|
||||
module.set_id("GalacticaContentModule");
|
||||
|
||||
let ct = ct_src.clone();
|
||||
let _ = FuncRegistration::new("get_outfit")
|
||||
.with_namespace(FnNamespace::Internal)
|
||||
.set_into_module(&mut module, move |outfit_name: ImmutableString| {
|
||||
let outfit = ct.outfits.get(&outfit_name.clone().into());
|
||||
|
||||
match outfit {
|
||||
Some(o) => OutfitState::new(o.clone()),
|
||||
None => {
|
||||
error!("called `ct::get_outfit` with an invalid outfit `{outfit_name:?}`");
|
||||
OutfitState::new_none()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutfitState {
|
||||
outfit: Option<Arc<Outfit>>,
|
||||
}
|
||||
|
||||
impl OutfitState {
|
||||
pub fn new(outfit: Arc<Outfit>) -> Self {
|
||||
Self {
|
||||
outfit: Some(outfit),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_none() -> Self {
|
||||
Self { outfit: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for OutfitState {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("OutfitState")
|
||||
.with_fn("is_some", |s: &mut Self| s.outfit.is_some())
|
||||
.with_fn("display_name", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.display_name.clone())
|
||||
.unwrap_or("".to_string())
|
||||
})
|
||||
.with_fn("desc", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.desc.clone())
|
||||
.unwrap_or("".to_string())
|
||||
})
|
||||
.with_fn("index", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.index.to_string())
|
||||
.unwrap_or("".to_string())
|
||||
})
|
||||
.with_fn("thumbnail", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.thumbnail.index.to_string())
|
||||
.unwrap_or("".to_string())
|
||||
})
|
||||
//
|
||||
// Stat getters
|
||||
//
|
||||
.with_fn("stat_thrust", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.stats.steer_power)
|
||||
.unwrap_or(0.0)
|
||||
})
|
||||
.with_fn("stat_steer_power", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.stats.steer_power)
|
||||
.unwrap_or(0.0)
|
||||
})
|
||||
.with_fn("stat_shield_strength", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.stats.shield_strength)
|
||||
.unwrap_or(0.0)
|
||||
})
|
||||
.with_fn("stat_shield_generation", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.stats.shield_generation)
|
||||
.unwrap_or(0.0)
|
||||
})
|
||||
.with_fn("stat_shield_delay", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| x.stats.shield_delay)
|
||||
.unwrap_or(0.0)
|
||||
})
|
||||
.with_fn("stat_shield_dps", |s: &mut Self| {
|
||||
s.outfit
|
||||
.as_ref()
|
||||
.map(|x| {
|
||||
if x.gun.is_some() {
|
||||
(1.0 / x.gun.as_ref().unwrap().rate)
|
||||
* x.gun.as_ref().unwrap().projectile.damage
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
})
|
||||
.unwrap_or(0.0)
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
mod conf;
|
||||
mod ct;
|
||||
mod radialbar;
|
||||
mod scrollbox;
|
||||
mod sprite;
|
||||
|
@ -6,6 +7,7 @@ mod textbox;
|
|||
mod ui;
|
||||
|
||||
pub use conf::build_conf_module;
|
||||
pub use ct::{build_ct_module, OutfitState};
|
||||
pub use radialbar::build_radialbar_module;
|
||||
pub use scrollbox::build_scrollbox_module;
|
||||
pub use sprite::build_sprite_module;
|
||||
|
|
|
@ -62,6 +62,32 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
|||
}
|
||||
});
|
||||
|
||||
let state = state_src.clone();
|
||||
let ct = ct_src.clone();
|
||||
let _ = FuncRegistration::new("set_sprite")
|
||||
.with_namespace(FnNamespace::Internal)
|
||||
.set_into_module(
|
||||
&mut module,
|
||||
move |name: ImmutableString, sprite: ImmutableString| {
|
||||
let mut ui_state = state.borrow_mut();
|
||||
|
||||
match ui_state.get_mut_by_name(&name) {
|
||||
Some(UiElement::Sprite(x)) => {
|
||||
let m = ct.sprites.get(&sprite.clone().into()).clone();
|
||||
if m.is_none() {
|
||||
error!("called `sprite::set_sprite` with an invalid sprite `{sprite}`");
|
||||
return;
|
||||
}
|
||||
x.set_sprite(m.unwrap().clone())
|
||||
}
|
||||
|
||||
_ => {
|
||||
error!("called `sprite::set_sprite` on an invalid name `{sprite}`")
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let state = state_src.clone();
|
||||
let ct = ct_src.clone();
|
||||
let _ = FuncRegistration::new("set_mask")
|
||||
|
@ -198,6 +224,7 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
|||
e.set_color(x);
|
||||
});
|
||||
|
||||
// TODO: fix click collider when preserving aspect
|
||||
let state = state_src.clone();
|
||||
let _ = FuncRegistration::new("preserve_aspect")
|
||||
.with_namespace(FnNamespace::Internal)
|
||||
|
@ -221,8 +248,9 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
|||
e.set_preserve_aspect(x);
|
||||
});
|
||||
|
||||
// TODO: maybe remove?
|
||||
let state = state_src.clone();
|
||||
let _ = FuncRegistration::new("set_disable_events")
|
||||
let _ = FuncRegistration::new("disable_events")
|
||||
.with_namespace(FnNamespace::Internal)
|
||||
.set_into_module(&mut module, move |name: ImmutableString, x: bool| {
|
||||
let mut ui_state = state.borrow_mut();
|
||||
|
@ -230,13 +258,13 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
|||
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||
UiElement::Sprite(x) => x,
|
||||
_ => {
|
||||
error!("called `sprite::set_disable_events` on an invalid name `{name}`");
|
||||
error!("called `sprite::disable_events` on an invalid name `{name}`");
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some(UiElement::Sprite(x)) => x,
|
||||
_ => {
|
||||
error!("called `sprite::set_disable_events` on an invalid name `{name}`");
|
||||
error!("called `sprite::disable_events` on an invalid name `{name}`");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,8 +23,8 @@ pub fn build_textbox_module(
|
|||
move |name: ImmutableString,
|
||||
font_size: f32,
|
||||
line_height: f32,
|
||||
rect: Rect,
|
||||
color: Color| {
|
||||
color: Color,
|
||||
rect: Rect| {
|
||||
let mut ui_state = state.borrow_mut();
|
||||
|
||||
if ui_state.contains_name(&name) {
|
||||
|
@ -72,6 +72,32 @@ pub fn build_textbox_module(
|
|||
},
|
||||
);
|
||||
|
||||
let state = state_src.clone();
|
||||
let font = font_src.clone();
|
||||
let _ = FuncRegistration::new("set_color")
|
||||
.with_namespace(FnNamespace::Internal)
|
||||
.set_into_module(&mut module, move |name: ImmutableString, color: Color| {
|
||||
let mut ui_state = state.borrow_mut();
|
||||
|
||||
let e = match ui_state.get_mut_by_name(&name) {
|
||||
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||
UiElement::Text(x) => x,
|
||||
_ => {
|
||||
error!("called `textbox::set_color` on an invalid name `{name}`");
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
Some(UiElement::Text(x)) => x,
|
||||
_ => {
|
||||
error!("called `textbox::set_color` on an invalid name `{name}`");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
e.set_color(&mut font.borrow_mut(), color);
|
||||
});
|
||||
|
||||
let state = state_src.clone();
|
||||
let font = font_src.clone();
|
||||
let _ = FuncRegistration::new("align_left")
|
||||
|
|
|
@ -10,19 +10,11 @@ pub enum Anchor {
|
|||
}
|
||||
|
||||
#[export_module]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub mod anchor_mod {
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Center: Anchor = Anchor::Center;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const NorthWest: Anchor = Anchor::NorthWest;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const NorthEast: Anchor = Anchor::NorthEast;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const SouthWest: Anchor = Anchor::SouthWest;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const SouthEast: Anchor = Anchor::SouthEast;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct Rect {
|
|||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn new(x: f32, y: f32, w: f32, h: f32, anchor_self: Anchor, anchor_parent: Anchor) -> Self {
|
||||
pub fn new(x: f32, y: f32, w: f32, h: f32, anchor_parent: Anchor, anchor_self: Anchor,) -> Self {
|
||||
Self {
|
||||
pos: Point2::new(x, y),
|
||||
dim: Vector2::new(w, h),
|
||||
|
|
|
@ -6,6 +6,7 @@ mod state;
|
|||
|
||||
pub use directive::*;
|
||||
pub use event::*;
|
||||
pub use functions::OutfitState;
|
||||
pub use helpers::{anchor::*, color::*, rect::*, vector::*};
|
||||
pub use state::*;
|
||||
|
||||
|
@ -52,6 +53,7 @@ pub fn register_into_engine(
|
|||
|
||||
// Modules
|
||||
engine.register_static_module("ui", functions::build_ui_module(state_src.clone()).into());
|
||||
engine.register_static_module("ct", functions::build_ct_module(ct_src.clone()).into());
|
||||
engine.register_static_module(
|
||||
"sprite",
|
||||
functions::build_sprite_module(ct_src.clone(), state_src.clone()).into(),
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use galactica_content::{Outfit, Ship, SystemObject};
|
||||
use galactica_content::{Ship, SystemObject};
|
||||
use galactica_system::{
|
||||
data::{self},
|
||||
phys::{objects::PhysShip, PhysSimShipHandle},
|
||||
};
|
||||
use galactica_util::to_degrees;
|
||||
use log::error;
|
||||
use nalgebra::Vector2;
|
||||
use rapier2d::dynamics::RigidBody;
|
||||
use rhai::{Array, CustomType, Dynamic, ImmutableString, TypeBuilder};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{Color, UiVector};
|
||||
use super::{functions::OutfitState, Color, UiVector};
|
||||
use crate::{RenderInput, RenderState};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -89,18 +90,16 @@ impl CustomType for ShipState {
|
|||
.with_fn("content_index", |s: &mut Self| {
|
||||
s.get_content().display_name.clone()
|
||||
})
|
||||
.with_fn("thumbnail", |s: &mut Self| {
|
||||
s.get_content().thumbnail.clone()
|
||||
.with_fn("thumbnail", |s: &mut Self| -> ImmutableString {
|
||||
s.get_content().thumbnail.index.clone().into()
|
||||
})
|
||||
.with_fn("landed_on", |s: &mut Self| s.landed_on())
|
||||
.with_fn("get_shields", |s: &mut Self| {
|
||||
.with_fn("current_shields", |s: &mut Self| {
|
||||
s.get_ship().get_data().get_shields()
|
||||
})
|
||||
.with_fn("get_total_shields", |s: &mut Self| {
|
||||
s.get_ship().get_data().get_outfits().get_total_shields()
|
||||
})
|
||||
.with_fn("get_total_hull", |s: &mut Self| s.get_content().hull)
|
||||
.with_fn("get_hull", |s: &mut Self| {
|
||||
.with_fn("total_hull", |s: &mut Self| s.get_content().hull)
|
||||
.with_fn("empty_mass", |s: &mut Self| s.get_content().mass)
|
||||
.with_fn("current_hull", |s: &mut Self| {
|
||||
s.get_ship().get_data().get_hull()
|
||||
})
|
||||
.with_fn("get_size", |s: &mut Self| s.get_content().size)
|
||||
|
@ -113,25 +112,37 @@ impl CustomType for ShipState {
|
|||
let h = s.get_ship().get_data().get_faction();
|
||||
let c = h.color;
|
||||
Color::new(c[0], c[1], c[2], 1.0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutfitState {
|
||||
outfit: Arc<Outfit>,
|
||||
}
|
||||
|
||||
impl OutfitState {}
|
||||
|
||||
impl CustomType for OutfitState {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("OutfitState")
|
||||
.with_fn("display_name", |s: &mut Self| s.outfit.display_name.clone())
|
||||
.with_fn("index", |s: &mut Self| s.outfit.index.to_string())
|
||||
.with_fn("thumbnail", |s: &mut Self| {
|
||||
s.outfit.thumbnail.index.to_string()
|
||||
})
|
||||
//
|
||||
// Stat getters
|
||||
//
|
||||
.with_fn("stat_thrust", |s: &mut Self| {
|
||||
s.get_ship()
|
||||
.get_data()
|
||||
.get_outfits()
|
||||
.get_stats()
|
||||
.engine_thrust
|
||||
})
|
||||
.with_fn("stat_steer_power", |s: &mut Self| {
|
||||
s.get_ship()
|
||||
.get_data()
|
||||
.get_outfits()
|
||||
.get_stats()
|
||||
.steer_power
|
||||
})
|
||||
.with_fn("stat_shield_strength", |s: &mut Self| {
|
||||
s.get_ship()
|
||||
.get_data()
|
||||
.get_outfits()
|
||||
.get_stats()
|
||||
.shield_strength
|
||||
})
|
||||
.with_fn("stat_max_shield_generation", |s: &mut Self| {
|
||||
s.get_ship()
|
||||
.get_data()
|
||||
.get_outfits()
|
||||
.get_stats()
|
||||
.shield_generation
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +164,7 @@ impl SystemObjectState {
|
|||
.unwrap()
|
||||
.outfitter
|
||||
{
|
||||
a.push(Dynamic::from(OutfitState { outfit: o.clone() }));
|
||||
a.push(Dynamic::from(OutfitState::new(o.clone())));
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
@ -234,6 +245,7 @@ impl CustomType for SystemObjectState {
|
|||
pub struct State {
|
||||
input: Arc<RenderInput>,
|
||||
window_aspect: f32,
|
||||
window_size: Vector2<u32>,
|
||||
}
|
||||
|
||||
// TODO: remove this
|
||||
|
@ -245,6 +257,7 @@ impl State {
|
|||
Self {
|
||||
input: input.clone(),
|
||||
window_aspect: state.window_aspect,
|
||||
window_size: Vector2::new(state.window_size.width, state.window_size.height),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +297,12 @@ impl CustomType for State {
|
|||
.with_fn("player_ship", Self::player_ship)
|
||||
.with_fn("ships", Self::ships)
|
||||
.with_fn("objects", Self::objects)
|
||||
.with_fn("window_aspect", |s: &mut Self| s.window_aspect);
|
||||
.with_fn("window_aspect", |s: &mut Self| s.window_aspect)
|
||||
.with_fn("window_size", |s: &mut Self| {
|
||||
UiVector::new(
|
||||
s.window_size.x as f32 / s.input.ct.config.ui_scale,
|
||||
s.window_size.y as f32 / s.input.ct.config.ui_scale,
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ impl UiSprite {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_sprite(&mut self, sprite: Arc<Sprite>) {
|
||||
self.anim = SpriteAutomaton::new(sprite);
|
||||
}
|
||||
|
||||
pub fn set_mask(&mut self, mask: Option<Arc<Sprite>>) {
|
||||
self.mask = mask;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,11 @@ impl UiTextBox {
|
|||
self.reflow(font);
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, font: &mut FontSystem, color: api::Color) {
|
||||
self.color = color;
|
||||
self.reflow(font);
|
||||
}
|
||||
|
||||
pub fn set_align(&mut self, font: &mut FontSystem, align: Align) {
|
||||
self.justify = align;
|
||||
self.reflow(font);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use galactica_content::{ContentIndex, GunPoint, Outfit, OutfitSpace};
|
||||
use galactica_content::{ContentIndex, GunPoint, Outfit, OutfitSpace, OutfitStats};
|
||||
|
||||
/// Possible outcomes when adding an outfit
|
||||
pub enum OutfitAddResult {
|
||||
|
@ -61,12 +61,17 @@ pub struct OutfitSet {
|
|||
/// if value is Some, this point is taken.
|
||||
gun_points: HashMap<GunPoint, Option<Arc<Outfit>>>,
|
||||
|
||||
/// Outfit values
|
||||
/// This isn't strictly necessary, but we don't want to
|
||||
/// re-compute this on each frame.
|
||||
engine_thrust: f32,
|
||||
steer_power: f32,
|
||||
shield_strength: f32,
|
||||
/// The combined stats of all outfits in this set.
|
||||
/// There are two things to note here:
|
||||
/// First, shield_delay is always zero. That is handled
|
||||
/// seperately, since it is different for every outfit.
|
||||
/// Second, shield_generation represents the MAXIMUM POSSIBLE
|
||||
/// shield generation, after all delays have expired.
|
||||
///
|
||||
/// Note that this field isn't strictly necessary... we could compute stats
|
||||
/// by iterating over the outfits in this set. We don't want to do this every
|
||||
/// frame, though, so we keep track of the total sum here.
|
||||
stats: OutfitStats,
|
||||
|
||||
/// All shield generators in this outfit set
|
||||
// These can't be summed into one value, since each has a
|
||||
|
@ -81,10 +86,7 @@ impl OutfitSet {
|
|||
total_space: available_space,
|
||||
used_space: OutfitSpace::new(),
|
||||
gun_points: gun_points.iter().map(|x| (x.clone(), None)).collect(),
|
||||
|
||||
engine_thrust: 0.0,
|
||||
steer_power: 0.0,
|
||||
shield_strength: 0.0,
|
||||
stats: OutfitStats::zero(),
|
||||
shield_generators: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -111,13 +113,12 @@ impl OutfitSet {
|
|||
|
||||
self.used_space += o.space;
|
||||
|
||||
self.engine_thrust += o.engine_thrust;
|
||||
self.steer_power += o.steer_power;
|
||||
self.shield_strength += o.shield_strength;
|
||||
self.stats.add(&o.stats);
|
||||
|
||||
self.shield_generators.push(ShieldGenerator {
|
||||
outfit: o.clone(),
|
||||
delay: o.shield_delay,
|
||||
generation: o.shield_generation,
|
||||
delay: o.stats.shield_delay,
|
||||
generation: o.stats.shield_generation,
|
||||
});
|
||||
|
||||
if self.outfits.contains_key(&o.index) {
|
||||
|
@ -143,9 +144,7 @@ impl OutfitSet {
|
|||
|
||||
self.used_space -= o.space;
|
||||
|
||||
self.engine_thrust -= o.engine_thrust;
|
||||
self.steer_power -= o.steer_power;
|
||||
self.shield_strength -= o.shield_strength;
|
||||
self.stats.subtract(&o.stats);
|
||||
|
||||
{
|
||||
// This index will exist, since we checked the hashmap
|
||||
|
@ -183,11 +182,6 @@ impl OutfitSet {
|
|||
self.shield_generators.iter()
|
||||
}
|
||||
|
||||
/// Get maximum possible shield regen
|
||||
pub fn get_max_shield_regen(&self) -> f32 {
|
||||
self.shield_generators.iter().map(|x| x.generation).sum()
|
||||
}
|
||||
|
||||
/// Get the outfit attached to the given gun point
|
||||
/// Will panic if this gunpoint is not in this outfit set.
|
||||
pub fn get_gun(&self, point: &GunPoint) -> Option<Arc<Outfit>> {
|
||||
|
@ -204,18 +198,13 @@ impl OutfitSet {
|
|||
&self.used_space
|
||||
}
|
||||
|
||||
/// Total foward thrust
|
||||
pub fn get_engine_thrust(&self) -> f32 {
|
||||
self.engine_thrust
|
||||
}
|
||||
|
||||
/// Total steer power
|
||||
pub fn get_steer_power(&self) -> f32 {
|
||||
self.steer_power
|
||||
}
|
||||
|
||||
/// Total shield strength
|
||||
pub fn get_total_shields(&self) -> f32 {
|
||||
self.shield_strength
|
||||
/// Get the combined stats of all outfits in this set.
|
||||
/// There are two things to note here:
|
||||
/// First, shield_delay is always zero. That is handled
|
||||
/// seperately, since it is different for every outfit.
|
||||
/// Second, shield_generation represents the MAXIMUM POSSIBLE
|
||||
/// shield generation, after all delays have expired.
|
||||
pub fn get_stats(&self) -> &OutfitStats {
|
||||
&self.stats
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ impl ShipData {
|
|||
/// Add an outfit to this ship
|
||||
pub fn add_outfit(&mut self, o: &Arc<Outfit>) -> super::OutfitAddResult {
|
||||
let r = self.outfits.add(o);
|
||||
self.shields = self.outfits.get_total_shields();
|
||||
self.shields = self.outfits.get_stats().shield_strength;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -245,8 +245,8 @@ impl ShipData {
|
|||
}
|
||||
|
||||
// Regenerate shields
|
||||
if self.shields != self.outfits.get_total_shields() {
|
||||
self.shields = self.outfits.get_total_shields();
|
||||
if self.shields != self.outfits.get_stats().shield_strength {
|
||||
self.shields = self.outfits.get_stats().shield_strength;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,12 +260,12 @@ impl ShipData {
|
|||
|
||||
// Regenerate shields
|
||||
let time_since = self.last_hit.elapsed().as_secs_f32();
|
||||
if self.shields != self.outfits.get_total_shields() {
|
||||
if self.shields != self.outfits.get_stats().shield_strength {
|
||||
for g in self.outfits.iter_shield_generators() {
|
||||
if time_since >= g.delay {
|
||||
self.shields += g.generation * t;
|
||||
if self.shields > self.outfits.get_total_shields() {
|
||||
self.shields = self.outfits.get_total_shields();
|
||||
if self.shields > self.outfits.get_stats().shield_strength {
|
||||
self.shields = self.outfits.get_stats().shield_strength;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,21 +390,21 @@ impl PhysShip {
|
|||
if self.controls.thrust {
|
||||
rigid_body.apply_impulse(
|
||||
vector![engine_force.x, engine_force.y]
|
||||
* self.data.get_outfits().get_engine_thrust(),
|
||||
* self.data.get_outfits().get_stats().engine_thrust,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
if self.controls.right {
|
||||
rigid_body.apply_torque_impulse(
|
||||
self.data.get_outfits().get_steer_power() * -100.0 * res.t,
|
||||
self.data.get_outfits().get_stats().steer_power * -100.0 * res.t,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
if self.controls.left {
|
||||
rigid_body.apply_torque_impulse(
|
||||
self.data.get_outfits().get_steer_power() * 100.0 * res.t,
|
||||
self.data.get_outfits().get_stats().steer_power * 100.0 * res.t,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue