Schemes

From Mod Wiki

Revision as of 15:36, 8 September 2010 by Gannebamm (Talk | contribs)
Jump to: navigation, search
thanks to barin for explaining this!
Schemes are the basis of AIs brain. Other then logic which is usually written for one specific npc and one very special area and purpose a scheme could be used from every npc or mutant in online gameworld dynamicly (i know some of this could be done with logic, too).
This article should give you a general idea of how schemes could be used:

Structure___

Each scheme consists of four parts
evaluator - evaluates (thus the name) conditions which have to be met to perform action; each action may have more then one evaluator, but that depends on action itself
action - describes what happens with object, what actions it should perform; action is executed only when all evaluators binded to this action returns true (or false - that depends)
manager - all npcs have access to special object called planner (manager)  (returned by function motivation_action_manager) which runs in background and checks all evaluators; if certain evaluator (or group of evaluators) succeeds then assigned (binded to those evaluators) action is performed; to find action and evaluator, planner is using constants, that is evaluators and actions have special (unique) ids: for evaluators such ids are called properties and for actions - operators, so in other words, those are just numeric values which identifies evaluators and actions.

Example___

______________________
-- xr_test.script (had to be written)
----------------------------------------------------------------------------------------------------------------------
-- EVALUATORs
----------------------------------------------------------------------------------------------------------------------
class "evaluator_name" (property_evaluator) -- is evaluator
function evaluator_name:__init(name, storage, npc) super (nil, name) -- inheritance stuff, just copy it :)
	self.a = storage -- assign storage "in" npc for evaluator
end
function evaluator_name:evaluate()	-- this function will be called each game update
	local npc = self.object	-- self.object is online gameobject (see lua_info.script or online offline alife article for more info)
	debug_utils.debug_write("test_eval for %s", tostring(npc:character_name()))
 
	if npc:character_community() == "zombied" then -- if npc is zombie then dont use action
		return false
	end
	if xr_wounded.is_wounded(npc) then -- if npc is wounded then dont use action
		return false
	end
 
	if gbamm.test == true then -- if variable test in gbamm.script is true then use action (for every npc not wounded nor zombie)
		return true
	else return false	-- otherwise dont use action
	end
end
 
----------------------------------------------------------------------------------------------------------------------
--ACTIONs
----------------------------------------------------------------------------------------------------------------------
class "action_name" (action_base) -- is action
function action_name:__init (npc_name,action_name, storage) super (nil, action_name) -- inheritance stuff, just copy it :)
	self.a = storage -- assign storage "in" npc for action 
end
function action_name:initialize()	-- initialize stuff needed for the action called >>once<< action is evaluated true
	local npc = self.object	-- as in evaluator self.object == online game object
	action_base.initialize(self)
	npc:set_desired_position()	-- clear npcs before used desired position and direction
	npc:set_desired_direction()
 
	--thats a check for getting nearest for npcs accessible level vertex id of actor(player)
	local lvid = db.actor:level_vertex_id()
		if not npc:accessible(lvid) then
			local dummy = vector():set(0, 0, 0)
			lvid = npc:accessible_nearest(level.vertex_position(lvid), dummy)
		end
	end
 
	npc:set_dest_level_vertex_id(lvid)	-- set new target for npcs movement
	npc:set_path_type(game_object.level_path) -- set path for npcs movement
	state_mgr.set_state(npc, "assault") -- set npcs state to assault (look into state_lib.script for states)
end
function action_name:execute ()	-- execute the action (called >>updated = repeated in intervalls<< since action initialized, till "action:add_effect" (see below) is called )
	local npc = self.object -- same as above
	action_base.execute(self) -- execute the action
end
function action_name:finalize () -- finalize action called once if action:add_effect is used
	action_base.finalize(self) -- finish action
end
 
----------------------------------------------------------------------------------------------------------------------
-- BINDER
----------------------------------------------------------------------------------------------------------------------
function add_to_binder(npc, char_ini, scheme, section, st) -- is called from xr_logic.script see below
	-- list of stuff which had added to binder
 
	-- unique ids
	local evid_test = 25000 --properties id (evaluator)
	local acid_test = 25000 --operators id (actions)
	local manager = npc:motivation_action_manager()
 
	-- Evaluators
	manager:add_evaluator (evid_test, 		evaluator_name("eval_name", st))
 
	-- Actions
	local action = action_name (npc:name(),"action_name", st)
	-- preconditions when action is executed
	action:add_precondition		(world_property(stalker_ids.property_alive, true)) -- had to be alive
	action:add_precondition		(world_property(stalker_ids.property_enemy,	false)) -- no enemy in memory
	action:add_precondition		(world_property(stalker_ids.property_danger,false)) -- no danger in memory
	action:add_precondition		(world_property(stalker_ids.property_anomaly,false)) -- no anomaly in memory ?
	action:add_precondition		(world_property(evid_test,	true)) -- evid_test (property = our evaluators ID) had to return true
	-- when action shall be stopped
	action:add_effect (world_property(evid_test, 			false)) --  if evid_test returns false then stop action
	-- what action to add
	manager:add_action (acid_test, action)	-- add our operator with its ID (acid_test)
 
	action = manager:action (xr_actions_id.alife)
	action:add_precondition		(world_property(evid_test,		false)) -- ?? 
end
 
function set_test(npc, ini, scheme, section)	-- this is called from xr_logic.script in "function enable_generic_schemes(...)"
	local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section) -- calls through xr_logic.script function add_to_binder above
end
-- xr_test.script ends
_____________________
-- in xr_logic.script
...
function enable_generic_schemes(ini, npc, stype, section)
	if stype == modules.stype_stalker then
...
--insert
		xr_test.set_test (npc, ini, "test")
...
elseif stype == modules.stype_mobile then ...
-- xr_logic.script ends
________________
--modules.script
...
load_scheme("xr_reach_task",					"reach_task",		stype_stalker)
-- insert
load_scheme("xr_test",							"test",			stype_stalker)
...
-- modules.script ends

This is a work in progress, it will be updated with more content soon.

-- new content:
  • quote from talk with barin

To force nps to shoot (in SHOC):

self.object:set_item(shoot_type, weapon, NUMBER, direction)
> - shoot_type - one of the flags from object class (check lua help) - this  is basically a one shot, or a burst etc.
> - weapon - weapon object, best to use self.object:best_weapon() so game will select best weapon for npc
> - NUMBER - no idea really, try 1
> - direction - position to shoot at (for instance db.actor:position() to fire towards actor)
for instance:
> self.object:set_item(object.fire1, self.object:best_weapon(), 1,db.actor:position())
download this file to test schemes in COP: http://www.file-upload.net/download-2807474/scheme_scripts.zip.html

--Gannebamm 17:28, 8 September 2010 (EEST)

Personal tools