From cefebd3d2c203d5e05b0ec91d41faf01b8376632 Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Sat, 30 Mar 2013 21:37:17 +0000
Subject: [PATCH] [Add] Initial hooks support.

---
 dat/missions/cargo.lua |  24 +++++++++-
 src/hook.c             | 106 +++++++++++++++++++++++++++++++++++++++++
 src/hook.h             |  14 ++++++
 src/misn_lua.c         |  24 ++++++++++
 4 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 src/hook.c
 create mode 100644 src/hook.h

diff --git a/dat/missions/cargo.lua b/dat/missions/cargo.lua
index 2d669b0..ea9ba8a 100644
--- a/dat/missions/cargo.lua
+++ b/dat/missions/cargo.lua
@@ -1,3 +1,4 @@
+-- Create the mission.
 function create()
   -- Target destination.
   planet = space.getPlanet(misn.factions())
@@ -11,7 +12,26 @@ function create()
   carg.type = rnd.setDesc(string.format(
                           "%s needs a rush delivery of %d tons of %s by %s.",
                           planet, carg_mass, carg_type, "SOMEDAY"))
-  misn_setReward(string.format("%d Scred",
-                 carg_mass * 1000 + rnd.int(0, 5000)))
+  misn_reward = carg_mass * 1000 + rnd.int(0, 5000)
+  misn.setReward(string.format("%d Scred" misn_reward))
+
+  -- Set the hooks.
+  hook.land("land")
+end
+
+function accept()
+  player.addCargo(carg_type, carg_mass)
+  toolkit.msg("Mission Accepted",
+        string.format("The workers load the %d tons of %s onto your ship."
+        carg_mass, carg_type))
+end
+
+function land()
+  if planet.name() == planet then
+    player.rmCargo(carg_type, carg_mass)
+    player.pay(misn_reward)
+    toolkit.msg("Mission Accomplished",
+          string.format("The workers unload the %s at the docks.", carg_type))
+  end
 end
 
diff --git a/src/hook.c b/src/hook.c
new file mode 100644
index 0000000..0d94a83
--- /dev/null
+++ b/src/hook.c
@@ -0,0 +1,106 @@
+#include <malloc.h>
+#include <string.h>
+
+#include "log.h"
+#include "lephisto.h"
+#include "hook.h"
+
+// The hook.
+typedef struct Hook_ {
+  int id;
+  lua_State* L;
+  char* parent;
+  char* func;
+  char* stack;
+} Hook;
+
+// The stack.
+static int    hook_id     = 0; // Unique hook id.
+static Hook*  hook_stack  = NULL;
+static int    hook_mstack = 0;
+static int    hook_nstack = 0;
+
+int hook_run(Hook* hook) {
+  lua_State* L;
+
+  L = hook->L;
+
+  lua_getglobal(L, hook->func);
+  if(lua_pcall(L, 0, 0, 0))
+    // Error has accured.
+    WARN("Hook [%s] '%s' -> '%s' : %s", hook->stack,
+        hook->parent, hook->func, lua_tostring(L, -1));
+
+  return 0;
+}
+
+// Add/Remove hooks.
+int hook_add(lua_State* L, char* parent, char* func, char* stack) {
+  Hook* new_hook;
+
+  // If the memory must grow.
+  if(hook_nstack+1 > hook_mstack) {
+    hook_stack = realloc(hook_stack, (hook_mstack+5)*sizeof(Hook));
+    hook_mstack += 5;
+  }
+
+  // Create the new hook.
+  new_hook = &hook_stack[hook_nstack];
+  new_hook->id      = ++hook_id;
+  new_hook->L       = L;
+  new_hook->parent  = parent;
+  new_hook->func    = func;
+  new_hook->stack   = stack;
+
+  hook_nstack++;
+
+  return new_hook->id;
+}
+
+void hook_rm(int id) {
+  int l, m, h;
+  l = 0;
+  h = hook_nstack-1;
+
+  while(l <= h) {
+    m = (l+h)/2;
+    if(hook_stack[m].id > id) h = m-1;
+    else if(hook_stack[m].id < id) l = m+1;
+    else break;
+  }
+
+  if(m == (hook_nstack-1)) return;
+
+  // Move it!
+  memmove(&hook_stack[m+1], &hook_stack[m+2], hook_nstack-(m+1));
+  hook_nstack--;
+}
+
+void hook_rmParent(char* parent) {
+  int i;
+  for(i = 0; i < hook_nstack; i++)
+    if(strcmp(parent, hook_stack[i].parent) == 0) {
+      hook_rm(hook_stack[i].id);
+      i--;
+    }
+}
+
+// Run all hooks off the stack.
+int hooks_run(char* stack) {
+  int i;
+  for(i = 0; i < hook_nstack; i++)
+    if(strcmp(stack, hook_stack[i].stack)==0)
+      hook_run(&hook_stack[i]);
+
+  return 0;
+}
+
+// Clean upi after ourselves.
+void hook_cleanup(void) {
+  free(hook_stack);
+  hook_stack  = NULL;
+  // Sane defaults just in case.
+  hook_nstack = 0;
+  hook_mstack = 0;
+}
+
diff --git a/src/hook.h b/src/hook.h
new file mode 100644
index 0000000..18385c0
--- /dev/null
+++ b/src/hook.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "lua.h"
+
+// Add/Run hooks.
+int hook_add(lua_State* L, char* parent, char* func, char* stack);
+void hook_rm(int id);
+void hook_rmParent(char* parent);
+
+// Run hook.
+int hooks_run(char* stack);
+
+// Destroy hook.
+void hook_cleanup(void);
+
diff --git a/src/misn_lua.c b/src/misn_lua.c
index 98ab2a2..a1f5ece 100644
--- a/src/misn_lua.c
+++ b/src/misn_lua.c
@@ -1,6 +1,7 @@
 #include "lua.h"
 #include "lauxlib.h"
 
+#include "hook.h"
 #include "mission.h"
 #include "log.h"
 #include "lephisto.h"
@@ -42,11 +43,18 @@ static const luaL_Reg rnd_methods[] = {
   { 0, 0 }
 };
 
+static int hook_land(lua_State* L);
+static const luaL_Reg hook_methods[] = {
+  { "land", hook_land },
+  { 0, 0 },
+};
+
 // Register all the libaries.
 int misn_loadLibs(lua_State* L) {
   luaL_register(L, "misn",    misn_methods);
   luaL_register(L, "space",   space_methods);
   luaL_register(L, "rnd",     rnd_methods);
+  luaL_register(L, "hook",    hook_methods);
   return 0;
 }
 
@@ -131,3 +139,19 @@ static rnd_int(lua_State* L) {
   return 1;
 }
 
+// -- HOOK --
+static int hook_land(lua_State* L) {
+  char* parent, *func;
+  
+  MIN_ARGS(1);
+
+  parent = cur_mission->data->name;
+  if(lua_isstring(L, -1)) func = (char*)lua_tostring(L, -1);
+  else {
+    WARN("Mission %s: trying to push non-valid function hook", parent);
+    return 0;
+  }
+  hook_add(L, parent, func, "land");
+  return 0;
+}
+