Skip to content

Commit

Permalink
Modding
Browse files Browse the repository at this point in the history
  • Loading branch information
jac3km4 committed Mar 24, 2022
1 parent 39ebb21 commit ae75b4d
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 22 deletions.
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ features = ["single_threaded"]

[dependencies.egui-hook]
git = "https://github.com/jac3km4/egui-hook"
rev = "v0.0.4"
rev = "v0.0.5"

[dependencies.memhack]
git = "https://github.com/jac3km4/memhack"
Expand Down
28 changes: 28 additions & 0 deletions doc/MODS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Modding

## example autoloot mod
The mod below checks for a target to loot every 30 frames.
To run it, save the code below in `ELEX2\system\plugins\crony\autoloot\main.rhai`.

```rhai
fn initial_state() {
#{ ticks: 0 }
}
fn on_frame(state) {
if state.ticks > 30 {
loot_when_possible();
state.ticks = 0;
}
state.ticks += 1;
}
fn loot_when_possible() {
let looked_at = entity::get_look_at();
if !entity::is_none(looked_at) {
log("looting!");
game::auto_loot(entity::get_player(), looked_at);
}
}
```
93 changes: 83 additions & 10 deletions src/host.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use std::any::TypeId;
use std::cell::RefCell;
use std::fmt::Debug;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::mpsc;
use std::{fs, io};

use egui::{Align2, Color32, Context, FontId, Key, Rounding, Sense, TextEdit, Vec2, Window};
use flexi_logger::{Cleanup, Criterion, FileSpec, LogSpecification, Logger, Naming, WriteMode};
use heck::ToSnakeCase;
use rhai::plugin::CallableFunction;
use rhai::{Dynamic, Engine, FnAccess, FnNamespace, Module, RegisterNativeFunction, Scope};
use rhai::*;

use crate::{elex, handlers};

Expand All @@ -18,6 +22,7 @@ pub struct ScriptHost {
is_active: bool,
engine: Engine,
scope: Scope<'static>,
mods: Vec<Mod>,
}

impl Default for ScriptHost {
Expand All @@ -40,6 +45,7 @@ impl Default for ScriptHost {
is_active: false,
engine,
scope: Scope::new(),
mods: vec![],
}
}
}
Expand All @@ -64,6 +70,14 @@ impl ScriptHost {
}
}

fn process_frame(&mut self) {
for mod_ in &mut self.mods {
let _res: Result<(), _> = self
.engine
.call_fn(&mut mod_.scope, &mod_.ast, "on_frame", vec![mod_.state.clone()]);
}
}

pub fn toggle(&mut self) {
self.is_active = !self.is_active;
}
Expand Down Expand Up @@ -103,11 +117,54 @@ impl ScriptHost {
);
module
}

fn verify_version() -> bool {
const SUPPORTED_VERSION_TS: u32 = 1647620648;
let found_version = elex::check_version();

if found_version == SUPPORTED_VERSION_TS {
log::info!("C.R.O.N.Y successfully initialized!");
true
} else {
log::error!("Unsupported game version ({found_version}), exiting!");
false
}
}

fn load_mods(&mut self) -> Result<(), io::Error> {
let dir = std::env::current_exe()?
.parent()
.expect("no exe parent")
.join("plugins")
.join("crony");
for entry in fs::read_dir(dir)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
let main = entry.path().join("main.rhai");
if main.exists() {
match self.init_mod(main.clone()) {
Ok(()) => log::info!("Successfully loaded {}", main.display()),
Err(err) => log::info!("Failed to initilize {}: {}", main.display(), err),
}
}
}
}

Ok(())
}

fn init_mod(&mut self, path: PathBuf) -> Result<(), Box<EvalAltResult>> {
let ast = self.engine.compile_file(path)?;
let mut scope = Scope::new();
let state = self.engine.call_fn(&mut scope, &ast, "initial_state", ())?;
self.mods.push(Mod::new(ast, scope, state));
Ok(())
}
}

impl egui_hook::App for ScriptHost {
fn render(&mut self, ctx: &Context) {
const DEFAULT_SIZE: Vec2 = Vec2::new(600., 320.);
self.process_frame();

let was_active = self.is_active();
if ctx.input().key_pressed(Key::Home) {
Expand All @@ -117,6 +174,8 @@ impl egui_hook::App for ScriptHost {
return;
}

const DEFAULT_SIZE: Vec2 = Vec2::new(600., 320.);

Window::new("CRONY GUI")
.default_size(DEFAULT_SIZE)
.show(ctx, |ui| {
Expand Down Expand Up @@ -165,15 +224,29 @@ impl egui_hook::App for ScriptHost {
.start()
.ok();

const SUPPORTED_VERSION_TS: u32 = 1647620648;
let found_version = elex::check_version();
Self::verify_version()
}

if found_version == SUPPORTED_VERSION_TS {
log::info!("C.R.O.N.Y successfully initialized!");
true
} else {
log::error!("Unsupported game version ({found_version}), exiting!");
false
fn setup(&mut self, _ctx: &Context) {
if let Err(err) = self.load_mods() {
log::warn!("Failed to load mods: {err}")
}
}
}

#[derive(Debug)]
struct Mod {
ast: AST,
scope: Scope<'static>,
state: Rc<RefCell<Dynamic>>,
}

impl Mod {
fn new(ast: AST, scope: Scope<'static>, state: Dynamic) -> Self {
Self {
ast,
scope,
state: Rc::new(RefCell::new(state)),
}
}
}
Expand Down

0 comments on commit ae75b4d

Please sign in to comment.