Bonus round
This commit is contained in:
parent
30d409149b
commit
e38e119562
154
src/main.rs
154
src/main.rs
@ -101,6 +101,7 @@ struct Object {
|
|||||||
item: Option<Item>,
|
item: Option<Item>,
|
||||||
always_visible: bool,
|
always_visible: bool,
|
||||||
level: i32,
|
level: i32,
|
||||||
|
equipment: Option<Equipment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Object {
|
impl Object {
|
||||||
@ -118,6 +119,7 @@ impl Object {
|
|||||||
item: None,
|
item: None,
|
||||||
always_visible: false,
|
always_visible: false,
|
||||||
level: 1,
|
level: 1,
|
||||||
|
equipment: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +204,56 @@ impl Object {
|
|||||||
pub fn distance(&self, x: i32, y: i32) -> f32 {
|
pub fn distance(&self, x: i32, y: i32) -> f32 {
|
||||||
(((x - self.x).pow(2) + (y - self.y).pow(2)) as f32).sqrt()
|
(((x - self.x).pow(2) + (y - self.y).pow(2)) as f32).sqrt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equip object and show a message about it
|
||||||
|
pub fn equip(&mut self, messages: &mut Messages) {
|
||||||
|
if self.item.is_none() {
|
||||||
|
messages.add(
|
||||||
|
format!("Can't equip {:?} because it's not an Item.", self),
|
||||||
|
RED,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(ref mut equipment) = self.equipment {
|
||||||
|
if !equipment.equipped {
|
||||||
|
equipment.equipped = true;
|
||||||
|
messages.add(
|
||||||
|
format!("Equipped {} on {}.", self.name, equipment.slot),
|
||||||
|
LIGHT_GREEN,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
messages.add(
|
||||||
|
format!("Can't equip {:?} because it's not an Equipment.", self),
|
||||||
|
RED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequip object and show a message about it
|
||||||
|
pub fn dequip(&mut self, messages: &mut Messages) {
|
||||||
|
if self.item.is_none() {
|
||||||
|
messages.add(
|
||||||
|
format!("Can't dequip {:?} because it's not an Item.", self),
|
||||||
|
RED,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(ref mut equipment) = self.equipment {
|
||||||
|
if equipment.equipped {
|
||||||
|
equipment.equipped = false;
|
||||||
|
messages.add(
|
||||||
|
format!("Dequipped {} from {}.", self.name, equipment.slot),
|
||||||
|
LIGHT_YELLOW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messages.add(
|
||||||
|
format!("Can't dequip {:?} because it's not an Equipment.", self),
|
||||||
|
RED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
@ -304,6 +356,30 @@ struct Transition {
|
|||||||
value: u32,
|
value: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An object that can be equipped, yielding bonuses
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
struct Equipment {
|
||||||
|
slot: Slot,
|
||||||
|
equipped: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
enum Slot {
|
||||||
|
LeftHand,
|
||||||
|
RightHand,
|
||||||
|
Head,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Slot {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Slot::LeftHand => write!(f, "left hand"),
|
||||||
|
Slot::RightHand => write!(f, "right hand"),
|
||||||
|
Slot::Head => write!(f, "head"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum PlayerAction {
|
enum PlayerAction {
|
||||||
TookTurn,
|
TookTurn,
|
||||||
@ -343,11 +419,13 @@ enum Item {
|
|||||||
Lightning,
|
Lightning,
|
||||||
Confuse,
|
Confuse,
|
||||||
Fireball,
|
Fireball,
|
||||||
|
Equipment,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UseResult {
|
enum UseResult {
|
||||||
UsedUp,
|
UsedUp,
|
||||||
Cancelled,
|
Cancelled,
|
||||||
|
UsedAndKept,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_keys(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec<Object>) -> PlayerAction {
|
fn handle_keys(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec<Object>) -> PlayerAction {
|
||||||
@ -638,6 +716,10 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>, level: u32) {
|
|||||||
),
|
),
|
||||||
item: Item::Confuse,
|
item: Item::Confuse,
|
||||||
},
|
},
|
||||||
|
Weighted {
|
||||||
|
weight: 1000,
|
||||||
|
item: Item::Equipment,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let item_choice = WeightedChoice::new(item_chances);
|
let item_choice = WeightedChoice::new(item_chances);
|
||||||
@ -721,6 +803,16 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>, level: u32) {
|
|||||||
object.item = Some(Item::Confuse);
|
object.item = Some(Item::Confuse);
|
||||||
object
|
object
|
||||||
}
|
}
|
||||||
|
Item::Equipment => {
|
||||||
|
// Create a sword
|
||||||
|
let mut object = Object::new(x, y, '/', "sword", SKY, false);
|
||||||
|
object.item = Some(Item::Equipment);
|
||||||
|
object.equipment = Some(Equipment {
|
||||||
|
equipped: false,
|
||||||
|
slot: Slot::RightHand,
|
||||||
|
});
|
||||||
|
object
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
item.always_visible = true;
|
item.always_visible = true;
|
||||||
@ -956,7 +1048,18 @@ fn inventory_menu(inventory: &[Object], header: &str, root: &mut Root) -> Option
|
|||||||
let options = if inventory.len() == 0 {
|
let options = if inventory.len() == 0 {
|
||||||
vec!["Inventory is empty.".into()]
|
vec!["Inventory is empty.".into()]
|
||||||
} else {
|
} else {
|
||||||
inventory.iter().map(|item| item.name.clone()).collect()
|
inventory
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
// Show additional information, in case it's equipped
|
||||||
|
match item.equipment {
|
||||||
|
Some(equipment) if equipment.equipped => {
|
||||||
|
format!("{} (on {})", item.name, equipment.slot)
|
||||||
|
}
|
||||||
|
_ => item.name.clone(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let inventory_index = menu(header, &options, INVENTORY_WIDTH, root);
|
let inventory_index = menu(header, &options, INVENTORY_WIDTH, root);
|
||||||
@ -1031,7 +1134,16 @@ fn pick_item_up(object_id: usize, game: &mut Game, objects: &mut Vec<Object>) {
|
|||||||
game
|
game
|
||||||
.messages
|
.messages
|
||||||
.add(format!("You picked up a {}!", item.name), GREEN);
|
.add(format!("You picked up a {}!", item.name), GREEN);
|
||||||
|
let index = game.inventory.len();
|
||||||
|
let slot = item.equipment.map(|e| e.slot);
|
||||||
game.inventory.push(item);
|
game.inventory.push(item);
|
||||||
|
|
||||||
|
// Automatically equip, if the corresponding equipment slot is unused
|
||||||
|
if let Some(slot) = slot {
|
||||||
|
if get_equipped_in_slot(slot, &game.inventory).is_none() {
|
||||||
|
game.inventory[index].equip(&mut game.messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1044,6 +1156,7 @@ fn use_item(inventory_id: usize, tcod: &mut Tcod, game: &mut Game, objects: &mut
|
|||||||
Lightning => cast_lightning,
|
Lightning => cast_lightning,
|
||||||
Confuse => cast_confuse,
|
Confuse => cast_confuse,
|
||||||
Fireball => cast_fireball,
|
Fireball => cast_fireball,
|
||||||
|
Equipment => toggle_equipment,
|
||||||
};
|
};
|
||||||
match on_use(inventory_id, tcod, game, objects) {
|
match on_use(inventory_id, tcod, game, objects) {
|
||||||
UseResult::UsedUp => {
|
UseResult::UsedUp => {
|
||||||
@ -1053,6 +1166,7 @@ fn use_item(inventory_id: usize, tcod: &mut Tcod, game: &mut Game, objects: &mut
|
|||||||
UseResult::Cancelled => {
|
UseResult::Cancelled => {
|
||||||
game.messages.add("Cancelled", WHITE);
|
game.messages.add("Cancelled", WHITE);
|
||||||
}
|
}
|
||||||
|
UseResult::UsedAndKept => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
game.messages.add(
|
game.messages.add(
|
||||||
@ -1197,6 +1311,28 @@ fn cast_fireball(
|
|||||||
UseResult::UsedUp
|
UseResult::UsedUp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_equipment(
|
||||||
|
inventory_id: usize,
|
||||||
|
_tcod: &mut Tcod,
|
||||||
|
game: &mut Game,
|
||||||
|
_objects: &mut [Object],
|
||||||
|
) -> UseResult {
|
||||||
|
let equipment = match game.inventory[inventory_id].equipment {
|
||||||
|
Some(equipment) => equipment,
|
||||||
|
None => return UseResult::Cancelled,
|
||||||
|
};
|
||||||
|
if equipment.equipped {
|
||||||
|
game.inventory[inventory_id].dequip(&mut game.messages);
|
||||||
|
} else {
|
||||||
|
// If the slot is already being used, dequip whatever is there first
|
||||||
|
if let Some(current) = get_equipped_in_slot(equipment.slot, &game.inventory) {
|
||||||
|
game.inventory[current].dequip(&mut game.messages);
|
||||||
|
}
|
||||||
|
game.inventory[inventory_id].equip(&mut game.messages);
|
||||||
|
}
|
||||||
|
UseResult::UsedAndKept
|
||||||
|
}
|
||||||
|
|
||||||
// Find closest enemy, up to a maximum range, and in the player's FOV
|
// Find closest enemy, up to a maximum range, and in the player's FOV
|
||||||
fn closest_monster(tcod: &Tcod, objects: &[Object], max_range: i32) -> Option<usize> {
|
fn closest_monster(tcod: &Tcod, objects: &[Object], max_range: i32) -> Option<usize> {
|
||||||
let mut closest_enemy = None;
|
let mut closest_enemy = None;
|
||||||
@ -1283,6 +1419,9 @@ fn target_monster(
|
|||||||
|
|
||||||
fn drop_item(inventory_id: usize, game: &mut Game, objects: &mut Vec<Object>) {
|
fn drop_item(inventory_id: usize, game: &mut Game, objects: &mut Vec<Object>) {
|
||||||
let mut item = game.inventory.remove(inventory_id);
|
let mut item = game.inventory.remove(inventory_id);
|
||||||
|
if item.equipment.is_some() {
|
||||||
|
item.dequip(&mut game.messages);
|
||||||
|
}
|
||||||
item.set_pos(objects[PLAYER].x, objects[PLAYER].y);
|
item.set_pos(objects[PLAYER].x, objects[PLAYER].y);
|
||||||
game
|
game
|
||||||
.messages
|
.messages
|
||||||
@ -1663,6 +1802,19 @@ fn from_dungeon_level(table: &[Transition], level: u32) -> u32 {
|
|||||||
.map_or(0, |transition| transition.value)
|
.map_or(0, |transition| transition.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_equipped_in_slot(slot: Slot, inventory: &[Object]) -> Option<usize> {
|
||||||
|
for (inventory_id, item) in inventory.iter().enumerate() {
|
||||||
|
if item
|
||||||
|
.equipment
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |e| e.equipped && e.slot == slot)
|
||||||
|
{
|
||||||
|
return Some(inventory_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tcod::system::set_fps(FPS);
|
tcod::system::set_fps(FPS);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user