mirror of
https://github.com/redstrate/Kawari.git
synced 2025-05-05 20:27:45 +00:00
Implement Fantasia and remaking your character
Everyone's favorite copying mechanism/purchasable item is now functional in Kawari. The item doesn't disappear once you use it, because there's no API for that yet.
This commit is contained in:
parent
cb146f173e
commit
e7fb661244
10 changed files with 192 additions and 26 deletions
|
@ -3,5 +3,10 @@ function onBeginLogin(player)
|
||||||
player:send_message("Welcome to Kawari!")
|
player:send_message("Welcome to Kawari!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Actions
|
||||||
registerAction(3, "actions/Sprint.lua")
|
registerAction(3, "actions/Sprint.lua")
|
||||||
registerAction(9, "actions/FastBlade.lua")
|
registerAction(9, "actions/FastBlade.lua")
|
||||||
|
|
||||||
|
-- Items
|
||||||
|
registerAction(6221, "items/Fantasia.lua")
|
||||||
|
|
||||||
|
|
8
resources/scripts/items/Fantasia.lua
Normal file
8
resources/scripts/items/Fantasia.lua
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
function doAction(player)
|
||||||
|
effects = EffectsBuilder()
|
||||||
|
|
||||||
|
-- TODO: match retail fantasia behavior
|
||||||
|
player:set_remake_mode("EditAppearance")
|
||||||
|
|
||||||
|
return effects
|
||||||
|
end
|
|
@ -3,7 +3,7 @@ use std::net::SocketAddr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use kawari::RECEIVE_BUFFER_SIZE;
|
use kawari::RECEIVE_BUFFER_SIZE;
|
||||||
use kawari::common::workdefinitions::CharaMake;
|
use kawari::common::workdefinitions::{CharaMake, RemakeMode};
|
||||||
use kawari::common::{GameData, ObjectId, timestamp_secs};
|
use kawari::common::{GameData, ObjectId, timestamp_secs};
|
||||||
use kawari::common::{Position, determine_initial_starting_zone};
|
use kawari::common::{Position, determine_initial_starting_zone};
|
||||||
use kawari::config::get_config;
|
use kawari::config::get_config;
|
||||||
|
@ -712,8 +712,9 @@ async fn client_loop(
|
||||||
let lua = lua.lock().unwrap();
|
let lua = lua.lock().unwrap();
|
||||||
let state = lua.app_data_ref::<ExtraLuaState>().unwrap();
|
let state = lua.app_data_ref::<ExtraLuaState>().unwrap();
|
||||||
|
|
||||||
|
let key = request.action_key;
|
||||||
if let Some(action_script) =
|
if let Some(action_script) =
|
||||||
state.action_scripts.get(&request.action_key)
|
state.action_scripts.get(&key)
|
||||||
{
|
{
|
||||||
lua.scope(|scope| {
|
lua.scope(|scope| {
|
||||||
let connection_data = scope
|
let connection_data = scope
|
||||||
|
@ -745,6 +746,8 @@ async fn client_loop(
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
tracing::warn!("Action {key} isn't scripted yet! Ignoring...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1056,6 +1059,40 @@ async fn client_loop(
|
||||||
CustomIpcData::ImportCharacter { service_account_id, path } => {
|
CustomIpcData::ImportCharacter { service_account_id, path } => {
|
||||||
database.import_character(*service_account_id, path);
|
database.import_character(*service_account_id, path);
|
||||||
}
|
}
|
||||||
|
CustomIpcData::RemakeCharacter { content_id, chara_make_json } => {
|
||||||
|
// overwrite it in the database
|
||||||
|
database.set_chara_make(*content_id, chara_make_json);
|
||||||
|
|
||||||
|
// reset flag
|
||||||
|
database.set_remake_mode(*content_id, RemakeMode::None);
|
||||||
|
|
||||||
|
// send response
|
||||||
|
{
|
||||||
|
send_packet::<CustomIpcSegment>(
|
||||||
|
&mut connection.socket,
|
||||||
|
&mut connection.state,
|
||||||
|
ConnectionType::None,
|
||||||
|
CompressionType::Uncompressed,
|
||||||
|
&[PacketSegment {
|
||||||
|
segment_type: SegmentType::KawariIpc,
|
||||||
|
data: SegmentData::KawariIpc {
|
||||||
|
data: CustomIpcSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: CustomIpcType::CharacterRemade,
|
||||||
|
option: 0,
|
||||||
|
timestamp: 0,
|
||||||
|
data: CustomIpcData::CharacterRemade {
|
||||||
|
content_id: *content_id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("The server is recieving a response or unknown custom IPC!")
|
panic!("The server is recieving a response or unknown custom IPC!")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::common::CustomizeData;
|
use crate::common::CustomizeData;
|
||||||
|
|
||||||
// TODO: this isn't really an enum in the game, nor is it a flag either. it's weird!
|
// TODO: this isn't really an enum in the game, nor is it a flag either. it's weird!
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum RemakeMode {
|
pub enum RemakeMode {
|
||||||
/// No remake options are available.
|
/// No remake options are available.
|
||||||
|
@ -14,6 +15,19 @@ pub enum RemakeMode {
|
||||||
EditAppearance = 4,
|
EditAppearance = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<i32> for RemakeMode {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Self::None),
|
||||||
|
1 => Ok(Self::EditAppearanceName),
|
||||||
|
4 => Ok(Self::EditAppearance),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Application/Network/WorkDefinitions/ClientSelectData.cs
|
/// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Application/Network/WorkDefinitions/ClientSelectData.cs
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ClientSelectData {
|
pub struct ClientSelectData {
|
||||||
|
@ -38,8 +52,6 @@ pub struct ClientSelectData {
|
||||||
pub model_ids: [u32; 10],
|
pub model_ids: [u32; 10],
|
||||||
pub equip_stain: [u32; 10],
|
pub equip_stain: [u32; 10],
|
||||||
pub glasses: [u32; 2],
|
pub glasses: [u32; 2],
|
||||||
pub unk15: i32,
|
|
||||||
pub unk16: i32,
|
|
||||||
pub remake_mode: RemakeMode, // TODO: upstream a comment about this to FFXIVClientStructs
|
pub remake_mode: RemakeMode, // TODO: upstream a comment about this to FFXIVClientStructs
|
||||||
/// If above 0, then a message warns the user that they have X minutes left to remake their character.
|
/// If above 0, then a message warns the user that they have X minutes left to remake their character.
|
||||||
pub remake_minutes_remaining: i32,
|
pub remake_minutes_remaining: i32,
|
||||||
|
@ -71,8 +83,6 @@ impl ClientSelectData {
|
||||||
self.model_ids.map(|x| x.to_string()),
|
self.model_ids.map(|x| x.to_string()),
|
||||||
self.equip_stain.map(|x| x.to_string()),
|
self.equip_stain.map(|x| x.to_string()),
|
||||||
self.glasses.map(|x| x.to_string()),
|
self.glasses.map(|x| x.to_string()),
|
||||||
self.unk15.to_string(),
|
|
||||||
self.unk16.to_string(),
|
|
||||||
(self.remake_mode as i32).to_string(),
|
(self.remake_mode as i32).to_string(),
|
||||||
self.remake_minutes_remaining.to_string(),
|
self.remake_minutes_remaining.to_string(),
|
||||||
self.voice_id.to_string(),
|
self.voice_id.to_string(),
|
||||||
|
|
|
@ -23,6 +23,8 @@ impl ReadWriteIpcSegment for CustomIpcSegment {
|
||||||
CustomIpcType::DeleteCharacter => 4,
|
CustomIpcType::DeleteCharacter => 4,
|
||||||
CustomIpcType::CharacterDeleted => 1,
|
CustomIpcType::CharacterDeleted => 1,
|
||||||
CustomIpcType::ImportCharacter => 132,
|
CustomIpcType::ImportCharacter => 132,
|
||||||
|
CustomIpcType::RemakeCharacter => 1024 + 8,
|
||||||
|
CustomIpcType::CharacterRemade => 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +56,10 @@ pub enum CustomIpcType {
|
||||||
CharacterDeleted = 0x10,
|
CharacterDeleted = 0x10,
|
||||||
/// Request to import a character backup
|
/// Request to import a character backup
|
||||||
ImportCharacter = 0x11,
|
ImportCharacter = 0x11,
|
||||||
|
/// Remake a character
|
||||||
|
RemakeCharacter = 0x12,
|
||||||
|
// Character has been remade
|
||||||
|
CharacterRemade = 0x13,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -117,6 +123,17 @@ pub enum CustomIpcData {
|
||||||
#[bw(map = write_string)]
|
#[bw(map = write_string)]
|
||||||
path: String,
|
path: String,
|
||||||
},
|
},
|
||||||
|
#[br(pre_assert(*magic == CustomIpcType::RemakeCharacter))]
|
||||||
|
RemakeCharacter {
|
||||||
|
content_id: u64,
|
||||||
|
#[bw(pad_size_to = 1024)]
|
||||||
|
#[br(count = 1024)]
|
||||||
|
#[br(map = read_string)]
|
||||||
|
#[bw(map = write_string)]
|
||||||
|
chara_make_json: String,
|
||||||
|
},
|
||||||
|
#[br(pre_assert(*magic == CustomIpcType::CharacterRemade))]
|
||||||
|
CharacterRemade { content_id: u64 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CustomIpcData {
|
impl Default for CustomIpcData {
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub enum ActionKind {
|
||||||
#[default]
|
#[default]
|
||||||
Nothing = 0x0,
|
Nothing = 0x0,
|
||||||
Normal = 0x1,
|
Normal = 0x1,
|
||||||
|
Item = 0x2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
|
|
@ -417,8 +417,6 @@ impl LobbyConnection {
|
||||||
let our_actor_id;
|
let our_actor_id;
|
||||||
let our_content_id;
|
let our_content_id;
|
||||||
|
|
||||||
dbg!(CharaMake::from_json(&character_action.json));
|
|
||||||
|
|
||||||
// tell the world server to create this character
|
// tell the world server to create this character
|
||||||
{
|
{
|
||||||
let ipc_segment = CustomIpcSegment {
|
let ipc_segment = CustomIpcSegment {
|
||||||
|
@ -537,7 +535,58 @@ impl LobbyConnection {
|
||||||
}
|
}
|
||||||
LobbyCharacterActionKind::Move => todo!(),
|
LobbyCharacterActionKind::Move => todo!(),
|
||||||
LobbyCharacterActionKind::RemakeRetainer => todo!(),
|
LobbyCharacterActionKind::RemakeRetainer => todo!(),
|
||||||
LobbyCharacterActionKind::RemakeChara => todo!(),
|
LobbyCharacterActionKind::RemakeChara => {
|
||||||
|
// tell the world server to turn this guy into a catgirl
|
||||||
|
{
|
||||||
|
let ipc_segment = CustomIpcSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: CustomIpcType::RemakeCharacter,
|
||||||
|
option: 0,
|
||||||
|
timestamp: 0,
|
||||||
|
data: CustomIpcData::RemakeCharacter {
|
||||||
|
content_id: character_action.content_id,
|
||||||
|
chara_make_json: character_action.json.clone(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = send_custom_world_packet(ipc_segment).await.unwrap();
|
||||||
|
|
||||||
|
// we intentionally don't care about the response right now, it's not expected to fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a confirmation that the remakewas successful
|
||||||
|
{
|
||||||
|
let ipc = ServerLobbyIpcSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: ServerLobbyIpcType::CharaMakeReply,
|
||||||
|
option: 0,
|
||||||
|
timestamp: 0,
|
||||||
|
data: ServerLobbyIpcData::CharaMakeReply {
|
||||||
|
sequence: character_action.sequence + 1,
|
||||||
|
unk1: 0x1,
|
||||||
|
unk2: 0x1,
|
||||||
|
action: LobbyCharacterActionKind::RemakeChara,
|
||||||
|
details: CharacterDetails {
|
||||||
|
actor_id: 0, // TODO: fill maybe?
|
||||||
|
content_id: character_action.content_id,
|
||||||
|
character_name: character_action.name.clone(),
|
||||||
|
origin_server_name: self.world_name.clone(),
|
||||||
|
current_server_name: self.world_name.clone(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
self.send_segment(PacketSegment {
|
||||||
|
segment_type: SegmentType::Ipc,
|
||||||
|
data: SegmentData::Ipc { data: ipc },
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
LobbyCharacterActionKind::SettingsUploadBegin => todo!(),
|
LobbyCharacterActionKind::SettingsUploadBegin => todo!(),
|
||||||
LobbyCharacterActionKind::SettingsUpload => todo!(),
|
LobbyCharacterActionKind::SettingsUpload => todo!(),
|
||||||
LobbyCharacterActionKind::WorldVisit => todo!(),
|
LobbyCharacterActionKind::WorldVisit => todo!(),
|
||||||
|
|
|
@ -28,7 +28,7 @@ use crate::{
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Actor, CharacterData, Event, Inventory, Item, LuaPlayer, StatusEffects, WorldDatabase, Zone,
|
Actor, CharacterData, Event, Inventory, Item, LuaPlayer, StatusEffects, WorldDatabase, Zone,
|
||||||
inventory::Container,
|
inventory::Container, lua::Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
@ -623,7 +623,12 @@ impl ZoneConnection {
|
||||||
player.queued_segments.clear();
|
player.queued_segments.clear();
|
||||||
|
|
||||||
for task in &player.queued_tasks {
|
for task in &player.queued_tasks {
|
||||||
self.change_zone(task.zone_id).await;
|
match task {
|
||||||
|
Task::ChangeTerritory { zone_id } => self.change_zone(*zone_id).await,
|
||||||
|
Task::SetRemakeMode(remake_mode) => self
|
||||||
|
.database
|
||||||
|
.set_remake_mode(player.player_data.content_id, *remake_mode),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
player.queued_tasks.clear();
|
player.queued_tasks.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl WorldDatabase {
|
||||||
|
|
||||||
// Create characters data table
|
// Create characters data table
|
||||||
{
|
{
|
||||||
let query = "CREATE TABLE IF NOT EXISTS character_data (content_id INTEGER PRIMARY KEY, name STRING, chara_make STRING, city_state INTEGER, zone_id INTEGER, pos_x REAL, pos_y REAL, pos_z REAL, rotation REAL, inventory STRING);";
|
let query = "CREATE TABLE IF NOT EXISTS character_data (content_id INTEGER PRIMARY KEY, name STRING, chara_make STRING, city_state INTEGER, zone_id INTEGER, pos_x REAL, pos_y REAL, pos_z REAL, rotation REAL, inventory STRING, remake_mode INTEGER);";
|
||||||
connection.execute(query, ()).unwrap();
|
connection.execute(query, ()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,16 +239,22 @@ impl WorldDatabase {
|
||||||
for (index, (content_id, actor_id)) in content_actor_ids.iter().enumerate() {
|
for (index, (content_id, actor_id)) in content_actor_ids.iter().enumerate() {
|
||||||
let mut stmt = connection
|
let mut stmt = connection
|
||||||
.prepare(
|
.prepare(
|
||||||
"SELECT name, chara_make, zone_id, inventory FROM character_data WHERE content_id = ?1",
|
"SELECT name, chara_make, zone_id, inventory, remake_mode FROM character_data WHERE content_id = ?1",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let result: Result<(String, String, u16, String), rusqlite::Error> = stmt
|
let result: Result<(String, String, u16, String, i32), rusqlite::Error> = stmt
|
||||||
.query_row((content_id,), |row| {
|
.query_row((content_id,), |row| {
|
||||||
Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
|
Ok((
|
||||||
|
row.get(0)?,
|
||||||
|
row.get(1)?,
|
||||||
|
row.get(2)?,
|
||||||
|
row.get(3)?,
|
||||||
|
row.get(4)?,
|
||||||
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Ok((name, chara_make, zone_id, inventory_json)) = result {
|
if let Ok((name, chara_make, zone_id, inventory_json, remake_mode)) = result {
|
||||||
let chara_make = CharaMake::from_json(&chara_make);
|
let chara_make = CharaMake::from_json(&chara_make);
|
||||||
|
|
||||||
let inventory: Inventory = serde_json::from_str(&inventory_json).unwrap();
|
let inventory: Inventory = serde_json::from_str(&inventory_json).unwrap();
|
||||||
|
@ -273,9 +279,7 @@ impl WorldDatabase {
|
||||||
model_ids: inventory.get_model_ids(game_data),
|
model_ids: inventory.get_model_ids(game_data),
|
||||||
equip_stain: [0; 10],
|
equip_stain: [0; 10],
|
||||||
glasses: [0; 2],
|
glasses: [0; 2],
|
||||||
unk15: 0,
|
remake_mode: RemakeMode::try_from(remake_mode).unwrap(),
|
||||||
unk16: 0,
|
|
||||||
remake_mode: RemakeMode::None,
|
|
||||||
remake_minutes_remaining: 0,
|
remake_minutes_remaining: 0,
|
||||||
voice_id: chara_make.voice_id,
|
voice_id: chara_make.voice_id,
|
||||||
unk20: 0,
|
unk20: 0,
|
||||||
|
@ -338,7 +342,7 @@ impl WorldDatabase {
|
||||||
// insert char data
|
// insert char data
|
||||||
connection
|
connection
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT INTO character_data VALUES (?1, ?2, ?3, ?4, ?5, 0.0, 0.0, 0.0, 0.0, ?6);",
|
"INSERT INTO character_data VALUES (?1, ?2, ?3, ?4, ?5, 0.0, 0.0, 0.0, 0.0, ?6, 0);",
|
||||||
(
|
(
|
||||||
content_id,
|
content_id,
|
||||||
name,
|
name,
|
||||||
|
@ -416,4 +420,24 @@ impl WorldDatabase {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
stmt.execute((content_id,)).unwrap();
|
stmt.execute((content_id,)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the remake mode for a character
|
||||||
|
pub fn set_remake_mode(&self, content_id: u64, mode: RemakeMode) {
|
||||||
|
let connection = self.connection.lock().unwrap();
|
||||||
|
|
||||||
|
let mut stmt = connection
|
||||||
|
.prepare("UPDATE character_data SET remake_mode=?1 WHERE content_id = ?2")
|
||||||
|
.unwrap();
|
||||||
|
stmt.execute((mode as i32, content_id)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the chara make JSON for a character
|
||||||
|
pub fn set_chara_make(&self, content_id: u64, chara_make_json: &str) {
|
||||||
|
let connection = self.connection.lock().unwrap();
|
||||||
|
|
||||||
|
let mut stmt = connection
|
||||||
|
.prepare("UPDATE character_data SET chara_make=?1 WHERE content_id = ?2")
|
||||||
|
.unwrap();
|
||||||
|
stmt.execute((chara_make_json, content_id)).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use mlua::{FromLua, Lua, LuaSerdeExt, UserData, UserDataMethods, Value};
|
use mlua::{FromLua, Lua, LuaSerdeExt, UserData, UserDataMethods, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{ObjectId, ObjectTypeId, Position, timestamp_secs},
|
common::{ObjectId, ObjectTypeId, Position, timestamp_secs, workdefinitions::RemakeMode},
|
||||||
ipc::zone::{
|
ipc::zone::{
|
||||||
ActionEffect, ActorSetPos, DamageElement, DamageKind, DamageType, EffectKind, EventPlay,
|
ActionEffect, ActorSetPos, DamageElement, DamageKind, DamageType, EffectKind, EventPlay,
|
||||||
ServerZoneIpcData, ServerZoneIpcSegment,
|
ServerZoneIpcData, ServerZoneIpcSegment,
|
||||||
|
@ -12,8 +12,9 @@ use crate::{
|
||||||
|
|
||||||
use super::{PlayerData, StatusEffects, Zone};
|
use super::{PlayerData, StatusEffects, Zone};
|
||||||
|
|
||||||
pub struct ChangeTerritoryTask {
|
pub enum Task {
|
||||||
pub zone_id: u16,
|
ChangeTerritory { zone_id: u16 },
|
||||||
|
SetRemakeMode(RemakeMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -21,7 +22,7 @@ pub struct LuaPlayer {
|
||||||
pub player_data: PlayerData,
|
pub player_data: PlayerData,
|
||||||
pub status_effects: StatusEffects,
|
pub status_effects: StatusEffects,
|
||||||
pub queued_segments: Vec<PacketSegment<ServerZoneIpcSegment>>,
|
pub queued_segments: Vec<PacketSegment<ServerZoneIpcSegment>>,
|
||||||
pub queued_tasks: Vec<ChangeTerritoryTask>,
|
pub queued_tasks: Vec<Task>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LuaPlayer {
|
impl LuaPlayer {
|
||||||
|
@ -100,7 +101,11 @@ impl LuaPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_territory(&mut self, zone_id: u16) {
|
fn change_territory(&mut self, zone_id: u16) {
|
||||||
self.queued_tasks.push(ChangeTerritoryTask { zone_id });
|
self.queued_tasks.push(Task::ChangeTerritory { zone_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_remake_mode(&mut self, mode: RemakeMode) {
|
||||||
|
self.queued_tasks.push(Task::SetRemakeMode(mode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +137,11 @@ impl UserData for LuaPlayer {
|
||||||
this.change_territory(zone_id);
|
this.change_territory(zone_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
methods.add_method_mut("set_remake_mode", |lua, this, mode: Value| {
|
||||||
|
let mode: RemakeMode = lua.from_value(mode).unwrap();
|
||||||
|
this.set_remake_mode(mode);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue