Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic event manipulation operations in the map editor #40

Merged
merged 32 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7d63d92
`MapView.events` is now a `Slab` instead of a `HashMap`
white-axe Sep 15, 2023
de670a5
Add mechanism for selecting events in the map
white-axe Sep 15, 2023
8bc28c1
Merge branch 'new-tilemap' into nt-event-editor
white-axe Sep 15, 2023
1647186
This arm is unreachable assuming `map.events` is sorted
white-axe Sep 15, 2023
b4e5cfa
Merge branch 'new-tilemap' into nt-event-editor
white-axe Sep 15, 2023
8271ee4
Remove redundant type annotation
white-axe Sep 15, 2023
2524886
Selected event is now highlighted in the hover tooltip
white-axe Sep 15, 2023
5917b83
Apply texture bleeding fix to events as well
white-axe Sep 15, 2023
4d11e37
Don't interact with events if event layer isn't selected
white-axe Sep 15, 2023
4c533cf
Event editor now opens when double-clicking events
white-axe Sep 16, 2023
fd3d3ff
I was wrong; this line is not safe
white-axe Sep 16, 2023
32d90c9
Fix name editing in the event editor
white-axe Sep 16, 2023
6da4fe8
Events can now also be selected by the yellow square cursor
white-axe Sep 16, 2023
d45f2d0
Implement event deletion when pressing delete on the map
white-axe Sep 16, 2023
bfdb95f
Event editor windows now have (mostly) unique IDs
white-axe Sep 16, 2023
d3b8b28
Use a vector to store the events instead of a slab
white-axe Sep 17, 2023
35e29f1
Double-click on map tiles without events to create one
white-axe Sep 17, 2023
136e205
Enter key can now also be used for event creation/editing
white-axe Sep 17, 2023
f201bab
Fix incorrect serialization length for `OptionVec`
white-axe Sep 17, 2023
8a4ef34
Drag-and-drop events to move them on the map
white-axe Sep 17, 2023
3ea2dbb
Remove unused variables from map_view.rs
white-axe Sep 17, 2023
79087da
Event drag-and-drop now preserves the event offset from the cursor
white-axe Sep 17, 2023
afdd001
Apply clippy recommendations
white-axe Sep 17, 2023
6eddc6e
Apply another clippy recommendation
white-axe Sep 17, 2023
bec1a79
Fix misleading comment in src/tabs/map.rs
white-axe Sep 17, 2023
bce08f2
Don't move events when the mouse is not hovering over them
white-axe Sep 17, 2023
40d96ad
Simplify deserialization implementation for OptionVec
white-axe Sep 17, 2023
fb36e0b
Remove `pub` from `event_drag_offset`
white-axe Sep 18, 2023
4d2f442
Fix crash when adding event on a map with no events
white-axe Sep 18, 2023
e7b2ab3
Prevent event from sticking to cursor after double-clicking
white-axe Sep 18, 2023
0c5d4b4
Selected event border is now yellow instead of magenta
white-axe Sep 18, 2023
96f8299
Show magenta border around events that are being edited
white-axe Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions rmxp-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ pub mod rmxp;
// Shared structs with the same layout
mod shared;

mod option_vec;

mod rgss_structs;

mod helpers;

pub use helpers::*;
pub use option_vec::OptionVec;
pub use rgss_structs::{Color, Table1, Table2, Table3, Tone};

pub mod rpg {
Expand Down
248 changes: 248 additions & 0 deletions rmxp-types/src/option_vec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// Copyright (C) 2023 Lily Lyons
//
// This file is part of Luminol.
//
// Luminol is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Luminol is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Luminol. If not, see <http://www.gnu.org/licenses/>.

use std::ops::{Index, IndexMut};

use serde::ser::SerializeMap;

#[derive(Debug, Clone, PartialEq, Eq)]
/// A vector that can contain unused indices.
pub struct OptionVec<T> {
vec: Vec<Option<T>>,
num_values: usize,
}

pub struct Iter<'a, T> {
vec_iter: std::iter::Enumerate<std::slice::Iter<'a, Option<T>>>,
}

pub struct IterMut<'a, T> {
vec_iter: std::iter::Enumerate<std::slice::IterMut<'a, Option<T>>>,
}

pub struct Visitor<T>(std::marker::PhantomData<T>);

impl<T> OptionVec<T> {
/// Create a new OptionVec with no elements.
pub fn new() -> Self {
Self {
vec: Vec::new(),
num_values: 0,
}
}

pub fn len(&self) -> usize {
self.vec.len()
}

pub fn size(&self) -> usize {
self.num_values
}

pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}

pub fn get(&self, index: usize) -> Option<&T> {
self.vec.get(index).and_then(|x| x.as_ref())
}

pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
self.vec.get_mut(index).and_then(|x| x.as_mut())
}

pub fn capacity(&self) -> usize {
self.vec.capacity()
}

pub fn reserve(&mut self, additional: usize) {
self.vec.reserve(additional);
}

pub fn iter(&self) -> Iter<'_, T> {
self.into_iter()
}

pub fn iter_mut(&mut self) -> IterMut<'_, T> {
self.into_iter()
}

/// Write the element at the given index.
/// If there is already an element at the given index, it will be overwritten.
/// If there isn't, a new element will be added at that index.
pub fn insert(&mut self, index: usize, element: T) {
if index >= self.len() {
let additional = index - self.len() + 1;
self.reserve(additional);
self.vec
.extend(std::iter::repeat_with(|| None).take(additional));
}
if self.vec[index].is_none() {
self.num_values += 1;
}
self.vec[index] = Some(element);
}

/// Remove the element at the given index.
/// If the OptionVec is not big enough to contain this index, this will throw an error.
/// If there isn't an element at that index, this will throw an error.
pub fn try_remove(&mut self, index: usize) -> Result<(), String> {
if index >= self.len() {
Err(String::from("index out of bounds"))
} else if self.vec[index].is_none() {
Err(String::from("index not found"))
} else {
self.num_values -= 1;
self.vec[index] = None;
Ok(())
}
}

/// Remove the element at the given index.
/// If the OptionVec is not big enough to contain this index, this will panic.
/// If there isn't an element at that index, this will panic.
pub fn remove(&mut self, index: usize) {
self.try_remove(index).unwrap()
}
}

impl<T> Default for OptionVec<T> {
fn default() -> Self {
OptionVec::new()
}
}

impl<T> FromIterator<(usize, T)> for OptionVec<T> {
fn from_iter<I: IntoIterator<Item = (usize, T)>>(iterable: I) -> Self {
let mut vec = Vec::new();
let mut num_values = 0;
for (i, v) in iterable.into_iter() {
if i >= vec.len() {
let additional = i - vec.len() + 1;
vec.reserve(additional);
vec.extend(std::iter::repeat_with(|| None).take(additional));
}
vec[i] = Some(v);
num_values += 1;
}
Self { vec, num_values }
}
}

impl<T> Index<usize> for OptionVec<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
self.get(index).expect("index not found")
}
}

impl<T> IndexMut<usize> for OptionVec<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.get_mut(index).expect("index not found")
}
}

impl<'a, T> IntoIterator for &'a OptionVec<T> {
type Item = (usize, &'a T);
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
vec_iter: self.vec.iter().enumerate(),
}
}
}

impl<'a, T> IntoIterator for &'a mut OptionVec<T> {
type Item = (usize, &'a mut T);
type IntoIter = IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
vec_iter: self.vec.iter_mut().enumerate(),
}
}
}

impl<'a, T> Iterator for Iter<'a, T> {
type Item = (usize, &'a T);
fn next(&mut self) -> Option<Self::Item> {
for (index, element) in &mut self.vec_iter {
if let Some(element) = element {
return Some((index, element));
}
}
None
}
}

impl<'a, T> Iterator for IterMut<'a, T> {
type Item = (usize, &'a mut T);
fn next(&mut self) -> Option<Self::Item> {
for (index, element) in &mut self.vec_iter {
if let Some(element) = element {
return Some((index, element));
}
}
None
}
}

impl<'de, T> serde::de::Visitor<'de> for Visitor<T>
where
T: serde::Deserialize<'de>,
{
type Value = OptionVec<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a key-value mapping")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
std::iter::from_fn(|| map.next_entry().transpose()).collect()
}
}

impl<'de, T> serde::Deserialize<'de> for OptionVec<T>
where
T: serde::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_map(Visitor(std::marker::PhantomData))
}
}

impl<T> serde::Serialize for OptionVec<T>
where
T: serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut ser = serializer.serialize_map(Some(self.size()))?;
for (index, element) in self {
ser.serialize_key(&index)?;
ser.serialize_value(element)?;
}
ser.end()
}
}
4 changes: 2 additions & 2 deletions rmxp-types/src/rmxp/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with Luminol. If not, see <http://www.gnu.org/licenses/>.
use crate::rpg::{AudioFile, Event};
use crate::{id, Table3};
use crate::{id, option_vec, Table3};

#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename = "RPG::Map")]
Expand All @@ -31,5 +31,5 @@ pub struct Map {
pub encounter_list: Vec<i32>,
pub encounter_step: i32,
pub data: Table3,
pub events: slab::Slab<Event>,
pub events: option_vec::OptionVec<Event>,
}
11 changes: 11 additions & 0 deletions rmxp-types/src/shared/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ pub struct Event {
pub x: i32,
pub y: i32,
pub pages: Vec<EventPage>,

#[serde(skip)]
pub extra_data: EventExtraData,
}

#[derive(Debug, Default, Clone)]
pub struct EventExtraData {
/// Whether or not the event editor for this event is open
pub is_editor_open: bool,
}

impl Event {
Expand All @@ -36,6 +45,8 @@ impl Event {
x,
y,
pages: vec![EventPage::default()],

extra_data: EventExtraData::default(),
}
}
}
Expand Down
Loading
Loading