Merge branch 'master' into Controls

Controls
Tobias Zwick 2013-06-04 20:13:35 +02:00
commit 9027544c37
58 changed files with 1471 additions and 989 deletions

View File

@ -70,7 +70,6 @@ else()
SET(INITIAL_USE_OPEN_AL OFF)
endif()
option(USE_OPEN_AL "Use OpenAL to play sounds" ${INITIAL_USE_OPEN_AL})
option(DEBUGREC "Debug records" OFF)
option(OC_BUILD_MULTIPROCESSOR "Use all processor cores to build" OFF)
option(WITH_AUTOMATIC_UPDATE "Automatic updates are downloaded from the project website." OFF)

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>CanInsertMaterial</title>
<category>Landscape</category>
<subcat>Material</subcat>
<version>5.4 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>material_index</name>
<desc>Material to test to be inserted (see <funclink>Material</funclink>()).</desc>
</param>
<param>
<type>int</type>
<name>x</name>
<desc>X insert position or offset</desc>
<optional />
</param>
<param>
<type>int</type>
<name>y</name>
<desc>Y insert position or offset</desc>
<optional />
</param>
<param>
<type>proplist</type>
<name>out_insertpos</name>
<desc>If a writeable proplist is passed, members x and y are filled with the position at which the material would be inserted.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Tests if a material pixel at the given position can be inserted.</desc>
<remark>If the target position already contains material of the same density as the inserted material, the engine will search upwards for a proper insertion position.</remark>
<related>
<funclink>Material</funclink>
</related>
</func>
<author>Sven2</author><date>2001-11</date>
</funcs>

View File

@ -1,64 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>InsertMaterial</title>
<category>Landscape</category>
<subcat>Material</subcat>
<version>5.1 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>material_index</name>
<desc>Material to be inserted (see <funclink>Material</funclink>()).</desc>
</param>
<param>
<type>int</type>
<name>x</name>
<desc>X insert position or offset</desc>
<optional />
</param>
<param>
<type>int</type>
<name>y</name>
<desc>Y insert position or offset</desc>
<optional />
</param>
<param>
<type>int</type>
<name>xdir</name>
<desc>horizontal speed of material pixel to be inserted</desc>
<optional />
</param>
<param>
<type>int</type>
<name>ydir</name>
<desc>vertical speed of material pixel to be inserted</desc>
<optional />
</param>
<param>
<type>proplist</type>
<name>out_insertpos</name>
<desc>If a writeable proplist is passed, members x and y are filled with the actual insertion position.</desc>
<optional />
</param>
<param>
<type>bool</type>
<name>query_only</name>
<desc>If set, the material is not actually inserted. Use this if you are only interested if insertion would be possible and where it would take place.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Inserts a material pixel at the given position and given speed.</desc>
<remark>If the target position already contains material of the same density as the inserted material, the engine will search upwards for a proper insertion position. Use the parameter out_insertpos to find out where material was actually inserted.</remark>
<related>
<funclink>Material</funclink>
</related>
</func>
<author>Sven2</author><date>2001-11</date>
</funcs>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>InsertMaterial</title>
<category>Landscape</category>
<subcat>Material</subcat>
<version>5.1 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>material_index</name>
<desc>Material to be inserted (see <funclink>Material</funclink>()).</desc>
</param>
<param>
<type>int</type>
<name>x</name>
<desc>X insert position or offset</desc>
<optional />
</param>
<param>
<type>int</type>
<name>y</name>
<desc>Y insert position or offset</desc>
<optional />
</param>
<param>
<type>int</type>
<name>xdir</name>
<desc>horizontal speed of material pixel to be inserted</desc>
<optional />
</param>
<param>
<type>int</type>
<name>ydir</name>
<desc>vertical speed of material pixel to be inserted</desc>
<optional />
</param>
<param>
<type>proplist</type>
<name>out_insertpos</name>
<desc>If a writeable proplist is passed, members x and y are filled with the actual insertion position.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Inserts a material pixel at the given position and given speed.</desc>
<remark>If the target position already contains material of the same density as the inserted material, the engine will search upwards for a proper insertion position.</remark>
<related>
<funclink>Material</funclink>
</related>
</func>
<author>Sven2</author><date>2001-11</date>
</funcs>

View File

@ -53,6 +53,7 @@ var swim_comb = swim_down + 1;</code>
</example>
</examples>
<related>
<funclink>TransformBone</funclink>
<funclink>StopAnimation</funclink>
<funclink>SetAnimationPosition</funclink>
<funclink>SetAnimationWeight</funclink>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>SetAnimationBoneTransform</title>
<category>Animations</category>
<version>5.4 OC</version>
<syntax>
<rtype>int</rtype>
<params>
<param>
<type>int</type>
<name>animation_number</name>
<desc>Animation number of the animation whose bone transformation to change. The animation must have been created with <funclink>TransformBone</funclink>.</desc>
</param>
<param>
<type>array</type>
<name>transformation</name>
<desc>An array with 12 entries representing a 3x4 transformation matrix in row-major order. These matrices can be created via <funclink>Trans_Identity</funclink>, <funclink>Trans_Translate</funclink>, <funclink>Trans_Rotate</funclink> and <funclink>Trans_Scale</funclink> or they can be combined via <funclink>Trans_Mul</funclink>.</desc>
</param>
<param>
<type>int</type>
<name>attach_number</name>
<desc>If given, refers to the number of the attached mesh whose animation to change.</desc>
<optional />
</param>
</params>
</syntax>
<desc>This function can be used to change the transformation of a bone set with <funclink>TransformBone</funclink>. This allows to create dynamic animations by script. Returns <code>true</code> if the new transformation was set or <code>false</code> if there is no such animation node or it was not created with <funclink>TransformBone</funclink>.</desc>
<remark>See the <emlink href="definition/animations.html">animation documentation</emlink> for further explanations of the animation system.</remark>
<remark>The transformation passed to this function is not completely arbitrary, in particular it must not have components which skew the mesh along one of the axes. Skewing is not supported by the animation blending system. Skew matrices cannot be produced with one of the Trans_* functions directly, but it can result of the multiplication of a rotation matrix with a rotated scale matrix, e.g. <code><funclink>Trans_Mul</funclink>(<funclink>Trans_Rotate</funclink>(...), <funclink>Trans_Scale(...)</funclink>, <funclink>Trans_Rotate</funclink>(...))</code>. Skewing cannot occur by combining translation and rotation matrices only.</remark>
<examples>
<example>
<code><funclink>SetAnimationBoneTransform</funclink>(animation_number, <funclink>Trans_Rotate</funclink>(<funclink>FrameCounter</funclink>() % 360, 0, 1, 0));</code>
<text>Script for a timer to be called each frame: The bone for which the animation with <code>animation_number</code> was started is turning with one revolution per 360 frames.</text>
</example>
</examples>
<related>
<funclink>TransformBone</funclink>
<funclink>StopAnimation</funclink>
</related>
</func>
<author>Clonk-Karl</author><date>2013-05</date>
</funcs>

View File

@ -20,6 +20,12 @@
<name>position</name>
<desc>Specifies how to compute the position of the animation. The value needs to be created with one of the "Anim_" animation functions.</desc>
</param>
<param>
<type>int</type>
<name>attach_number</name>
<desc>If given, refers to the number of the attached mesh whose animation to change.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Sets a new position for the given animation. Returns <code>true</code> if the new AVP was set or <code>false</code> if there is no such animation with the given number or the number refers to a combination node.</desc>

View File

@ -20,6 +20,12 @@
<name>weight</name>
<desc>Specifies how to compute the weight of the animation in case the animation is combined with another animation in the given slot. The value needs to be created with one of the "Anim_" animation functions.</desc>
</param>
<param>
<type>int</type>
<name>attach_number</name>
<desc>If given, refers to the number of the attached mesh whose animation to change.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Sets a new weight for the given animation. Returns <code>true</code> if the new AVP was set or <code>false</code> if there is no such animation with the given number or the refernced node is an animation node.</desc>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>TransformBone</title>
<category>Animations</category>
<version>5.4 OC</version>
<syntax>
<rtype>int</rtype>
<params>
<param>
<type>string</type>
<name>bone</name>
<desc>Name of the bone to be transformed.</desc>
</param>
<param>
<type>array</type>
<name>transformation</name>
<desc>An array with 12 entries representing a 3x4 transformation matrix in row-major order. These matrices can be created via <funclink>Trans_Identity</funclink>, <funclink>Trans_Translate</funclink>, <funclink>Trans_Rotate</funclink> and <funclink>Trans_Scale</funclink> or they can be combined via <funclink>Trans_Mul</funclink>.</desc>
</param>
<param>
<type>int</type>
<name>slot</name>
<desc>Slot in the animation stack in which the animation should be inserted. See <emlink href="definition/animations.html">Animations</emlink>.</desc>
</param>
<param>
<type>array</type>
<name>weight</name>
<desc>Specifies how to compute the weight of the bone transformation in case it is combined with another animation in the given slot. The value needs to be created with one of the "Anim_" animation functions.</desc>
</param>
<param>
<type>int</type>
<name>sibling</name>
<desc>If the bone transformation is combined with another animation then this refers to the node with which the new node is combined. If not given or <code>nil</code> then the animation is combined with the animation at the top of the slot as returned by <funclink>GetRootAnimation</funclink>.</desc>
<optional />
</param>
</params>
</syntax>
<desc>This function is very similar to <funclink>PlayAnimation</funclink>. Instead of playing an animation which is pre-defined in the skeleton of the mesh, it allows individual bones to be transformed arbitrarily. The transformation is inserted as a leaf node into the animation tree for the given slot. The return value of this function is the animation number of the animation node inserted which can be used to manipulate or remove the animation later. If there are already animations in the given slot then additionally a combination node is created. This combination node is assigned the returned number plus 1.</desc>
<remark>See the <emlink href="definition/animations.html">animation documentation</emlink> for further explanations of the animation system.</remark>
<remark>The transformation passed to this function is not completely arbitrary, in particular it must not have components which skew the mesh along one of the axes. Skewing is not supported by the animation blending system. Skew matrices cannot be produced with one of the Trans_* functions directly, but it can result of the multiplication of a rotation matrix with a rotated scale matrix, e.g. <code><funclink>Trans_Mul</funclink>(<funclink>Trans_Rotate</funclink>(...), <funclink>Trans_Scale(...)</funclink>, <funclink>Trans_Rotate</funclink>(...))</code>. Skewing cannot occur by combining translation and rotation matrices only.</remark>
<examples>
<example>
<code><funclink>TransformBone</funclink>("skeleton_arm_upper.R", Trans_Rotate(90, 0, 1, 0), 5, <funclink>Anim_Const</funclink>(1000));</code>
<text>Rotates the right arm of a Clonk by 90 degrees around the Y axis (in bone coordinates).</text>
</example>
</examples>
<related>
<funclink>PlayAnimation</funclink>
<funclink>StopAnimation</funclink>
<funclink>SetAnimationBoneTransform</funclink>
</related>
</func>
<author>Clonk-Karl</author><date>2013-05</date>
</funcs>

View File

@ -85,8 +85,8 @@ global func FxDryTimeTimer(object pTarget, effect, int timer)
InsertMaterial(Material("Water"),Random(LandscapeWidth()-60)+30,1,Random(7)-3,100+Random(100));
return 1;
}
for(var i=0; i<6+Random(4);i++)
ExtractLiquid(310+Random(50),430+Random(10));
ExtractLiquidAmount(310+Random(50),430+Random(10),6+Random(4));
if(!GBackLiquid(335,430))
{
AddEffect("Rain",nil,100,2);

View File

@ -108,8 +108,7 @@ protected func FxIntLiquidDrainStart(object target, proplist effect, int tempora
protected func FxIntLiquidDrainTimer(object target, proplist effect)
{
for (var i = 0; i < effect.Strength / 2; i++)
ExtractLiquid(AbsX(effect.X), AbsY(effect.Y));
ExtractLiquidAmount(AbsX(effect.X), AbsY(effect.Y),effect.Strength / 2);
return 1;
}

View File

@ -28,12 +28,7 @@ private func Melt()
private func Freeze()
{
DoCon(1);
var i=2;
while(i>0)
{
ExtractLiquid();
i=--i;
}
ExtractMaterialAmount(0,0,Material("Water"),2);
}
local Collectible = 1;

View File

@ -1,6 +1,18 @@
/*-- Pipe line --*/
/*-- Pipe line
//Author: ST-DDT
Author: ST-DDT
--*/
local Name = "$Name$";
local ActMap = {
Connect = {
Prototype = Action,
Name = "Connect",
Procedure = DFA_CONNECT,
NextAction = "Connect"
}
};
protected func Initialize()
{
@ -11,19 +23,19 @@ protected func Initialize()
return;
}
// Returns true if this object is a functioning pipe.
/** Returns true if this object is a functioning pipe. */
public func IsPipeLine()
{
return GetAction() == "Connect";
}
// Returns whether this pipe is connected to an object.
/** Returns whether this pipe is connected to an object. */
public func IsConnectedTo(object obj)
{
return GetActionTarget(0) == obj || GetActionTarget(1) == obj;
}
// Returns the object which is connected to obj through this pipe.
/** Returns the object which is connected to obj through this pipe. */
public func GetConnectedObject(object obj)
{
if (GetActionTarget(0) == obj)
@ -50,62 +62,3 @@ private func BreakMessage()
line_end->Message("$TxtPipeBroke$");
return;
}
local ActMap = {
Connect = {
Prototype = Action,
Name = "Connect",
Procedure = DFA_CONNECT,
NextAction = "Connect"
}
};
/**
Extract liquid from barrel
@param sznMaterial: Material to extract; Wildcardsupport
@param inMaxAmount: Max Amount of Material being extracted
@param pnTarget: Object which extracts the liquid
@return [irMaterial,irAmount]
-irMaterial: Material being extracted
-irAmount: Amount being extracted
*/
public func GetLiquid(string sznMaterial, int inMaxAmount, object pnTarget, bool bWildcard)
{
var pConnected = GetConnectedObject(pnTarget);
if (!pConnected)
return ["", 0];
var aMat = pConnected->~LiquidOutput(sznMaterial, inMaxAmount, pnTarget, this, bWildcard);
//Bad script? Not needed.
if (GetType(aMat) != C4V_Array)
return [-1, 0];
//Verify data
if ((aMat[0] == "") || (GetLength(aMat) == 1))
aMat[1] = 0;
//Nothing is nothing
if (aMat[1] <= 0)
{
aMat[0] = "";
aMat[1] = 0;
} //Bad script end
return aMat;
}
/**
Insert liquid to barrel
@param sznMaterial: Material to insert
@param inMaxAmount: Max Amount of Material being inserted
@param pnSource: Object which inserts the liquid
@return inAmount: The inserted amount
*/
public func PutLiquid(string sznMaterial, int inMaxAmount, object pnSource)
{
var pConnected = GetConnectedObject(pnSource);
if (!pConnected)
return 0;
if (sznMaterial == "")
return 0;
return BoundBy(pConnected->~LiquidInput(sznMaterial, inMaxAmount, pnSource, this), 0, inMaxAmount);
}
local Name = "$Name$";

View File

@ -1,6 +1,13 @@
/*-- Pipe --*/
/*-- Pipe
//Author: ST-DDT
Author: ST-DDT
--*/
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local Rebuy = true;
protected func Hit()
{
@ -11,17 +18,13 @@ public func IsToolProduct() { return true; }
/*-- Line connection --*/
// Called with double dig: will connect power line to building at the clonk's position.
/** Will connect power line to building at the clonk's position. */
protected func ControlUse(object clonk, int x, int y)
{
// Is this already connected to a liquid pump?
if (FindObject(Find_PipeLine()))
if (FindObject(Find_Func("IsConnectedTo",this)))
return false;
// Only use if clonk is walking.
if (!clonk->IsWalking())
return true;
// Clonk should stand still.
clonk->SetComDir(COMD_Stop);
// Is there an object which accepts power lines?
var liquid_pump = FindObject(Find_AtPoint(), Find_Func("IsLiquidPump"));
// No liquid pump, display message.
@ -30,96 +33,33 @@ protected func ControlUse(object clonk, int x, int y)
clonk->Message("$MsgNoNewPipe$");
return true;
}
// already two pipes connected
if(liquid_pump->GetSource() && liquid_pump->GetDrain())
{
clonk->Message("$MsgHasPipes$");
return true;
}
// Create and connect pipe.
var pipe;
var pipe = CreateObject(PipeLine, 0, 0, NO_OWNER);
pipe->SetActionTargets(this, liquid_pump);
Sound("Connect");
// If liquid pump has no source yet, create one.
if (!liquid_pump->GetSource())
{
pipe = CreateObject(PipeLine, 0, 0, NO_OWNER);
pipe->SetActionTargets(this, liquid_pump);
liquid_pump->SetSource(pipe);
Sound("Connect");
clonk->Message("$MsgCreatedSource$");
return true;
}
// Otherwise if liquid pump has no drain, create one.
if (!liquid_pump->GetDrain())
else
{
pipe = CreateObject(PipeLine, 0, 0, NO_OWNER);
pipe->SetActionTargets(this, liquid_pump);
liquid_pump->SetDrain(pipe);
Sound("Connect");
clonk->Message("$MsgCreatedDrain$");
return true;
}
// Otherwise do nothing and notify player.
clonk->Message("MsgHasPipes");
return true;
}
// Finds all pipe lines connected to obj (can be nil in local calls).
private func Find_PipeLine(object obj)
{
if (!obj)
obj = this;
return [C4FO_Func, "IsConnectedTo", obj];
}
/**
Extract liquid from this
@param sznMaterial: Material to extract. 0 or "*" for any material.
@param inMaxAmount: Max Amount of Material being extracted
@param pnPump: Object which extracts the liquid
@param pnPipe: Pipe which extracts the liquid (connected to pnPump)
@param bnWildcard: Usefull to extract random liquids; use '*' for sznMaterial for all Materials
@return [irMaterial,irAmount]
-irMaterial: Material being extracted
-irAmount: Amount being extracted
*/
public func LiquidOutput(string sznMaterial, int inMaxAmount, object pnPump, object pnPipe, bool bnWildcard)
{
var itMaterial;
//Search liquid to pump
if (bnWildcard)
{
itMaterial = GetMaterial();
//nothing?
if (itMaterial == -1)
return ["", 0];
//no liquid?
if (GetMaterialVal("Density", "Material", itMaterial) != 25)
return ["", 0];
//wrong liquid?
if (sznMaterial)
if (!WildcardMatch(MaterialName(itMaterial),sznMaterial))
return ["", 0];
sznMaterial = MaterialName(itMaterial);
}
else
itMaterial = Material(sznMaterial);
if (GetMaterial() != itMaterial)
return ["", 0];
var itFound = ExtractMaterialAmount(0, 0, itMaterial, 5);
return [sznMaterial, itFound];
}
/**
Insert liquid to this
@param sznMaterial: Material to insert
@param inMaxAmount: Max Amount of Material being inserted
@param pnPump: Object which inserts the liquid
@param pnPipe: Pipe which inserts the liquid (connected to pnPump)
@return irAmount: The inserted amount
*/
public func LiquidInput(string sznMaterial, int inMaxAmount, object pnPump, object pnPipe)
{
var i=Max(0,inMaxAmount),itMaterial=Material(sznMaterial), n_inserted;
while (i--) if (InsertMaterial(itMaterial)) ++n_inserted; else break;
return n_inserted;
}
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local Rebuy = true;

View File

@ -450,6 +450,12 @@ global func MakePowerProducer(int amount /* the amount of power to produce const
return (Library_Power->GetPowerHelperForObject(this))->AddPowerProducer(this, amount);
}
/** Turns the power producer into an object that does not produce power */
global func UnmakePowerProducer()
{
MakePowerProducer(0);
}
// returns true if the current power balance is bigger or equal amount
global func IsPowerAvailable(int amount)
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

View File

@ -0,0 +1,25 @@
// pumpJack genrated by blender2ogre 0.6.0
material pumpJack
{
receive_shadows on
technique
{
pass pumpJack
{
ambient 0.5 0.5 0.5 1.0
diffuse 1 1 1 1.0
specular 0.5 0.5 0.5 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture pumpjack.png
tex_address_mode wrap
scale 1.0 1.0
colour_op modulate
}
}
}
}

View File

@ -1,8 +1,12 @@
/*--
Pump
Author: Maikel, ST-DDT, Sven2
Author: Maikel, ST-DDT, Sven2, Newton
Pumps liquids using drain and source pipes. Features include:
+ switch on and off
+ consume/produce a variable amount of power depending on the height of
source and drain
Pumps liquids using drain and source pipes.
--*/
#include Library_Structure
@ -10,51 +14,114 @@
#include Library_PowerConsumer
#include Library_PowerProducer
local current_pump_power; // Current power needed for pumping. Negative if power is being produced.
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 50;
local HitPoints = 70;
// This object is a liquid pump, thus pipes can be connected.
/*
States
"Wait": turned off or source pipe not connected
"WaitForPower": turned on but no power (does consume power)
"WaitForLiquid": turned on but no liquid (does not consume power)
"Pump": currently working and consuming/producing power
*/
local ActMap = {
Pump = {
Prototype = Action,
Name = "Pump",
Length = 30,
Delay = 3,
NextAction = "Pump",
StartCall = "CheckState",
PhaseCall = "Pumping"
},
Wait = {
Prototype = Action,
Name = "Wait",
Delay = 90,
NextAction = "Wait",
EndCall = "CheckState"
},
WaitForPower = {
Prototype = Action,
Name = "WaitForPower",
Delay = 30,
NextAction = "WaitForPower",
EndCall = "CheckState"
},
WaitForLiquid = {
Prototype = Action,
Name = "WaitForLiquid",
Delay = 30,
NextAction = "WaitForLiquid",
EndCall = "CheckState"
}
};
local animation; // animation handle
local switched_on; // controlled by Interaction. Indicates whether the user wants to pump or not
local powered; // whether the pump has enough power as a consumer, always true if producing
local power_used; // the amount of power currently consumed or (if negative) produced
local stored_material_index; //contained liquid
local stored_material_amount;
local source_pipe;
local drain_pipe;
/** This object is a liquid pump, thus pipes can be connected. */
public func IsLiquidPump() { return true; }
public func Construction(object creator)
func Definition(def)
{
return _inherited(creator, ...);
// for title image
SetProperty("PictureTransformation",Trans_Rotate(50,0,1,0),def);
// for building preview
SetProperty("MeshTransformation",Trans_Rotate(50,0,1,0),def);
}
protected func Initialize()
func Construction()
{
turned_on = true;
SetAction("Wait");
CheckCurrentState();
return;
// Rotate at a 45 degree angle towards viewer and add a litte bit of Random
this.MeshTransformation = Trans_Rotate(50 + RandomX(-10,10),0,1,0);
}
func Initialize()
{
switched_on = true;
var start = 0;
var end = GetAnimationLength("pump");
animation = PlayAnimation("pump", 5, Anim_Linear(GetAnimationPosition(animation), start, end, 35, ANIM_Loop), Anim_Const(1000));
SetState("Wait");
}
/*-- Interaction --*/
local turned_on;
public func IsInteractable() { return GetCon() >= 100; }
public func GetInteractionMetaInfo(object clonk)
{
if (turned_on)
if (switched_on)
return { Description = "$MsgTurnOff$", IconName = nil, IconID = Icon_Stop };
return { Description = "$MsgTurnOn$", IconName = nil, IconID = Icon_Play };
else
return { Description = "$MsgTurnOn$", IconName = nil, IconID = Icon_Play };
}
// On interaction the pump can be turned on or off.
/** Turn on or off. */
public func Interact(object clonk)
{
turned_on = !turned_on;
CheckCurrentState();
switched_on = !switched_on;
CheckState();
return true;
}
/*-- Pipe connection --*/
local source_pipe;
local drain_pipe;
// Set-Getters for source and drain pipe.
public func GetSource() { return source_pipe; }
public func SetDrain(object pipe) { drain_pipe = pipe; }
public func GetDrain() { return drain_pipe; }
@ -62,9 +129,11 @@ public func GetDrain() { return drain_pipe; }
public func SetSource(object pipe)
{
source_pipe = pipe;
CheckCurrentState();
CheckState();
}
/*-- Power stuff --*/
func QueryWaivePowerRequest()
{
// has less priority than other objects, but not too low
@ -73,333 +142,256 @@ func QueryWaivePowerRequest()
func OnNotEnoughPower()
{
// assert: current action is "Pump" or "PumpWaitLiquid"
SetActionKeepPhase("PumpWaitPower");
return _inherited(...);
_inherited(...);
powered = false;
CheckState();
}
func OnEnoughPower()
{
// assert: current action is either PumpWaitPower or Wait
SetActionKeepPhase("Pump");
return _inherited(...);
}
func PumpWaitPowerStart() { return AddTimer(this.PumpingWaitForPower, 60); }
func PumpWaitPowerStop() { return RemoveTimer(this.PumpingWaitForPower); }
func PumpingWaitForPower()
{
// Waiting for power: Check pump height, because we might become a producer
if (!source_pipe) return CheckCurrentState();
if (last_source_y != (source_pipe->GetConnectedObject(this) ?? this)->GetY() || last_drain_y != GetDrainObject()->GetY()) UpdatePumpHeight(true);
}
local aMaterials=["", 0]; //contained liquids
local pumpable_materials; // materials that can be pumped
local last_source_y, last_drain_y;
local pump_amount; // mat amount pumped since last height check
protected func PumpingWaitForLiquid()
{
// Pump is ready; check for liquids to pump
if (!source_pipe) return CheckCurrentState();
if(!HasLiquidToPump()) return;
// Check stuck drain
if (aMaterials[1] && (aMaterials[0] != "")) if (!InsertMaterial(Material(aMaterials[0]), 0,0,0,0, nil, true)) return;
// OK; let's pump
SetActionKeepPhase("Pump");
UpdatePumpHeight();
_inherited(...);
powered = true;
CheckState();
}
/** Returns object to which the liquid is pumped */
private func GetDrainObject()
{
if (drain_pipe) return drain_pipe->GetConnectedObject(this) ?? this;
return this;
}
/** Re turns object to which the liquid is pumped */
private func GetSourceObject()
{
if (source_pipe) return source_pipe->GetConnectedObject(this) ?? this;
return this;
}
/** Returns amount of pixels to pump per 30 frames */
public func GetPumpSpeed()
{
return 50;
}
/** PhaseCall of Pump: Pump the liquid from the source to the drain pipe */
protected func Pumping()
{
// at this point we can assert that we have power
// something went wrong in the meantime?
// let the central function handle that
if (!source_pipe)
return CheckCurrentState();
// nothing to pump right now?
if(!HasLiquidToPump())
{
UnmakePowerActuator();
SetActionKeepPhase("PumpWaitLiquid");
return;
}
// Recheck pump height
if (pump_amount > 200 || last_source_y != (source_pipe->GetConnectedObject(this) ?? this)->GetY() || last_drain_y != GetDrainObject()->GetY())
{
UpdatePumpHeight(true);
if (GetAction() != "Pump") return;
}
// let the central function handle that on next check
if (!source_pipe) return;
var pump_ok = true;
// is empty?
if ((aMaterials[1] == 0) || (aMaterials[0] == ""))
// is empty? -> try to get liquid
if (!stored_material_amount)
{
// get new materials
aMaterials = source_pipe->GetLiquid(pumpable_materials, 5, this, true);
var aMat = GetSourceObject()->ExtractLiquidAmount(0,0, GetPumpSpeed()/10);
// no material to pump?
if ((aMaterials[0] == "") || (aMaterials[1] == 0))
pump_ok = false;
if (aMat)
{
stored_material_index = aMat[0];
stored_material_amount = aMat[1];
}
else
pump_amount += aMaterials[1];
{
pump_ok = false;
}
}
if (pump_ok)
{
if (drain_pipe)
var i = stored_material_amount;
while (i > 0)
{
var pumped = BoundBy(drain_pipe->PutLiquid(aMaterials[0], aMaterials[1], this), 0, aMaterials[1]);
aMaterials[1] -= pumped;
// Drain is stuck?
if (!pumped) pump_ok = false;
if (GetDrainObject()->InsertMaterial(stored_material_index))
{
i--;
}
// Drain is stuck.
else
{
pump_ok = false;
break;
}
}
stored_material_amount = i;
if (stored_material_amount <= 0)
stored_material_index = nil;
}
if(!pump_ok)
{
SetState("WaitForLiquid");
}
}
/** Re check state and change the state if needed */
func CheckState()
{
var is_fullcon = GetCon() >= 100;
var can_pump = source_pipe && is_fullcon && switched_on;
// can't pump at all -> wait
if (!can_pump)
{
SetState("Wait");
}
else
{
// can pump but has no liquid -> wait for liquid
if (!HasLiquidToPump())
{
SetState("WaitForLiquid");
}
else
{
var i = Max(0, aMaterials[1]), itMaterial = Material(aMaterials[0]);
while (i)
if (InsertMaterial(itMaterial))
i--;
else
{
// Drain is stuck.
pump_ok = false;
break;
}
aMaterials[1] = i;
if (!i) aMaterials[0] = "";
// can pump, has liquid but has no power -> wait for power
if (!powered)
{
SetState("WaitForPower");
}
// otherwise, pump! :-)
else
{
SetState("Pump");
}
// regularly update the power usage while pumping or waiting for power
UpdatePowerUsage();
}
}
if (!pump_ok)
{
// Couldn't pump. Probably drain stuck.
UnmakePowerActuator();
SetActionKeepPhase("PumpWaitLiquid");
}
// maybe add the possebility to empty pump (invaild mats?)
return;
}
// Get current height the pump has to push liquids upwards (input.y - output.y)
func GetPumpHeight()
/** Get current height the pump has to push liquids upwards (input.y - output.y) */
private func GetPumpHeight()
{
var src = source_pipe->GetConnectedObject(this) ?? this, dst = GetDrainObject();
var out_pos = {X=dst->GetX(), Y=dst->GetY()};
dst->InsertMaterial(Material("Water"), 0,0,0,0, out_pos, true); // TODO assumes water material is loaded
var src_x = src->GetX(), src_y = src->GetY();
if (Global->GBackLiquid(src_x, src_y))
// compare each the surfaces of the bodies of liquid pumped
// find Y position of surface of liquid that is pumped to target
var source_y = 0;
if (GetSourceObject()->GBackLiquid())
{
var src_mat = Global->GetMaterial(src_x, src_y);
while (src_y>0 && src_mat == Global->GetMaterial(src_x, src_y-1)) --src_y;
var src_mat = GetSourceObject()->GetMaterial();
while (src_mat == GetSourceObject()->GetMaterial(0, source_y-1))
--source_y;
}
return src_y - out_pos.Y;
// same for target (use same function as if inserting)
var target_pos = {X=0, Y=0};
GetDrainObject()->CanInsertMaterial(Material("Water"),0,0,target_pos);
return (GetSourceObject()->GetY() + source_y) - target_pos.Y;
}
// Recheck power usage/production for current pump height
func UpdatePumpHeight(bool unmake_actuator)
/** Recheck power usage/production for current pump height
and make the pump a producer / consumer for the power system */
private func UpdatePowerUsage()
{
pump_amount = 0;
last_source_y = (source_pipe->GetConnectedObject(this) ?? this)->GetY();
last_drain_y = GetDrainObject()->GetY();
var new_power = PumpHeight2Power(GetPumpHeight());
if (unmake_actuator)
UnmakePowerActuator();
var new_power;
if(IsUsingPower())
new_power = PumpHeight2Power(GetPumpHeight());
else
if (new_power == current_pump_power) return true;
return MakePowerActuator(new_power);
}
// Makes this a power consumer or producer and sets appropriate waiting actions
func MakePowerActuator(int new_power)
{
new_power = 0;
// do nothing if not necessary
if (new_power == power_used)
return;
// and update energy system
if (new_power > 0)
{
SetActionKeepPhase("PumpWaitPower");
if (power_used < 0)
{
powered = false; // needed since the flag was set manually
UnmakePowerProducer();
}
MakePowerConsumer(new_power);
}
else if (new_power < 0)
{
if (power_used > 0)
UnmakePowerConsumer();
MakePowerProducer(-new_power);
powered = true; // when producing, we always have power
}
else // new_power == 0
{
if (power_used < 0) UnmakePowerProducer();
else if (power_used > 0) UnmakePowerConsumer();
powered = true;
}
power_used = new_power;
}
/** Return whether the pump should be using power in the current state */
private func IsUsingPower()
{
return switched_on && (GetAction() == "Pump" || GetAction() == "WaitForPower");
}
/** Transform pump height (input.y - output.y) to required power */
private func PumpHeight2Power(int pump_height)
{
// pumping downwards will only produce energy after an offset
var power_offset = 40;
// max power consumed/produced
var max_power = 150;
return BoundBy((pump_height + power_offset)/15*5, -max_power,max_power+power_offset);
}
/** Returns whether there is liquid at the source pipe to pump */
private func HasLiquidToPump()
{
if (!source_pipe)
return false;
// source
if(!GetSourceObject()->GBackLiquid())
return false;
// target (test with the very popular liquid "water")
if(!GetDrainObject()->CanInsertMaterial(Material("Water"),0,0))
return false;
return true;
}
/** Set the state of the pump, retaining the animation position and updating the power usage */
func SetState(string act)
{
if(act == GetAction()) return;
var start = 0;
var end = GetAnimationLength("pump");
var anim_pos = GetAnimationPosition(animation);
if (act == "Pump")
{
SetAnimationPosition(animation, Anim_Linear(anim_pos, start, end, 35, ANIM_Loop));
}
else if(act == "WaitForLiquid")
{
SetAnimationPosition(animation, Anim_Linear(anim_pos, start, end, 350, ANIM_Loop));
}
else
{
SetActionKeepPhase("Pump");
if (new_power < 0) MakePowerProducer(-new_power);
SetAnimationPosition(animation, Anim_Const(anim_pos));
}
current_pump_power = new_power;
return true;
}
func UnmakePowerActuator()
{
if (current_pump_power > 0)
UnmakePowerConsumer();
else if (current_pump_power < 0)
MakePowerProducer(0);
return true;
}
// Transform pump height (input.y - output.y) to required power
func PumpHeight2Power(int pump_height)
{
return BoundBy((pump_height + 35)/30*10, -150,150);
}
func CheckCurrentState()
{
if(turned_on)
// deactivate power usage when not pumping
if (powered && (act == "Wait" || act == "WaitForLiquid"))
{
var has_source_pipe = !!source_pipe;
var is_fullcon = GetCon() >= 100;
if(GetAction() == "Wait") // waiting: not consuming power
{
if(has_source_pipe && is_fullcon)
{
UpdatePumpHeight();
return;
}
else return; // waiting and no source pipe, keep waiting
}
else // not waiting: consuming power
{
if(!has_source_pipe || !is_fullcon)
{
UnmakePowerActuator();
SetAction("Wait");
return;
}
return;
}
// this point should not be reached
if (power_used < 0) UnmakePowerProducer();
else if(power_used > 0) UnmakePowerConsumer();
power_used = 0;
powered = false;
}
else // turned off
{
if(GetAction() != "Wait") // consuming power (except in PumpWaitLiquid condition)
{
UnmakePowerActuator();
SetAction("Wait");
return;
}
else // already waiting
return;
}
FatalError("Not every case handled in Pump::CheckCurrentState");
}
// Returns whether the pump can pump some liquid.
private func HasLiquidToPump()
{
// If there is no source pipe, return false.
if (!source_pipe)
return false;
// If there is nothing to pump at the source return false.
var source = source_pipe->GetConnectedObject(this);
if (!source)
return false;
if (!source->GBackLiquid())
return false;
// TODO: Account for pumping into buildings.
// Pumping is okay.
return true;
}
/**
Set name or wildcard string of materials this pump can pump
@param to_val: Material that can be pumped. 0 or "*" for any material.
*/
public func SetPumpableMaterials(string to_val)
{
pumpable_materials = to_val;
return true;
}
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 50;
local HitPoints = 70;
/*
"Pump": the pump is currently working and consuming power
"WillPump": the pump wants to work as soon as there is power
"Wait": the pump has been turned off or some other need is not fulfilled (f.e. connected pipe)
*/
local ActMap = {
Pump = {
Prototype = Action,
Name = "Pump",
Procedure = DFA_NONE,
Length = 30,
Delay = 3,
Directions = 2,
FlipDir = 1,
X = 0,
Y = 0,
Wdt = 28,
Hgt = 32,
NextAction = "Pump",
StartCall = "CheckCurrentState",
PhaseCall = "Pumping"
},
Wait = {
Prototype = Action,
Name = "Wait",
Procedure = DFA_NONE,
Length = 1,
Delay = 60,
Directions = 2,
FlipDir = 1,
X = 0,
Y = 0,
Wdt = 28,
Hgt = 32,
NextAction = "Wait",
StartCall = "CheckCurrentState"
},
PumpWaitPower = {
Prototype = Action,
Name = "PumpWaitPower",
Procedure = DFA_NONE,
Length = 30,
Delay = 0,
Directions = 2,
FlipDir = 1,
X = 0,
Y = 0,
Wdt = 28,
Hgt = 32,
StartCall = "PumpWaitPowerStart",
AbortCall = "PumpWaitPowerStop",
NextAction = "PumpWaitPower"
},
PumpWaitLiquid = { // Pump waiting for liquid: Move slowly to indicate we're turned on
Prototype = Action,
Name = "PumpWaitLiquid",
Procedure = DFA_NONE,
Length = 30,
Delay = 10,
Directions = 2,
FlipDir = 1,
X = 0,
Y = 0,
Wdt = 28,
Hgt = 32,
NextAction = "PumpWaitLiquid",
PhaseCall = "PumpingWaitForLiquid"
}
};
// Set action while retaining phase from last action
func SetActionKeepPhase(string act)
{
var phase = GetPhase();
if (!SetAction(act)) return false;
SetPhase(phase);
return true;
// finally, set the action
SetAction(act);
}

View File

@ -1,4 +1,4 @@
Name=Pumpe
Description=Die Pumpe kann beliebige Flüssigkeiten von A nach B pumpen.
Description=Die Pumpe kann beliebige Flüssigkeiten von A nach B pumpen. Pumpt sie Flüssigkeiten abwärts, erzeugt sie dabei sogar ein wenig Strom.
MsgTurnOff=Pumpe abschalten
MsgTurnOn=Pumpe anschalten

View File

@ -1,4 +1,4 @@
Name=Pump
Description=The pump can transport liquid from A to B.
Description=The pump can transport liquid from A to B. If it pumps liquids downwards, it even generates a bit of power.
MsgTurnOff=Turn off pump
MsgTurnOn=Turn on pump

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -8,7 +8,7 @@ Difficulty=50
Definition1=Objects.ocd
[Game]
Goals=Goal_SellGems=1;
Goals=Goal_RepairStatue=1;
Rules=Rule_TeamAccount=1;Rule_BuyAtFlagpole=1;
[Player1]

View File

@ -51,24 +51,42 @@ global func FindPosInMat(string sMat, int iXStart, int iYStart, int iWidth, int
return 0; // No location found.
}
// Removes a material pixel from the specified location, if the material is a liquid.
// \par x X coordinate. Offset if called in object context.
// \par y Y coordinate. Offset if called in object context.
// \returns The material index of the removed pixel, or -1 if no liquid was found.
/** Removes a material pixel from the specified location, if the material is a liquid.
@param x X coordinate
@param y Y coordinate
@return The material index of the removed pixel, or -1 if no liquid was found. */
global func ExtractLiquid(int x, int y)
{
var mat = GetMaterial(x, y);
var density = GetMaterialVal("Density", "Material", mat);
if (density < C4M_Liquid || density >= C4M_Solid)
return -1;
ExtractMaterialAmount(x, y, mat, 1);
return mat;
var result = ExtractLiquidAmount(x, y, 1);
if(!result) return -1;
return result[0];
}
// Removes a material pixel from the specified location, if the material is flammable.
// \par x X coordinate. Offset if called in object context.
// \par y Y coordinate. Offset if called in object context.
// \returns \c true if material was removed, \c false otherwise.
/** Tries to remove amount material pixels from the specified location if the material is a liquid.
@param x X coordinate
@param y Y coordinate
@param amount amount of liquid that should be extracted
@return an array with the first position being the material index being extracted and the second the
actual amount of pixels extracted OR nil if there was no liquid at all */
global func ExtractLiquidAmount(int x, int y, int amount)
{
var mat = GetMaterial(x, y);
if(mat == -1)
return nil;
var density = GetMaterialVal("Density", "Material", mat);
if (density < C4M_Liquid || density >= C4M_Solid)
return nil;
var amount = ExtractMaterialAmount(x, y, mat, amount);
if (amount <= 0)
return nil;
return [mat, amount];
}
/** Removes a material pixel from the specified location, if the material is flammable
@param x X coordinate. Offset if called in object context.
@param y Y coordinate. Offset if called in object context.
@return true if material was removed, false otherwise. */
global func FlameConsumeMaterial(int x, int y)
{
var mat = GetMaterial(x, y);

View File

@ -36,16 +36,13 @@ don't need to include this file or any of the files it includes. */
#include <boost/function.hpp>
#include <boost/bind.hpp>
#ifdef DEBUGREC
#define DEBUGREC_SCRIPT
#define DEBUGREC_START_FRAME 0
#define DEBUGREC_PXS
#define DEBUGREC_OBJCOM
#define DEBUGREC_MATSCAN
//#define DEBUGREC_RECRUITMENT
#define DEBUGREC_MENU
#define DEBUGREC_OCF
#endif
// solidmask debugging
//#define SOLIDMASK_DEBUG

View File

@ -62,6 +62,11 @@ public:
char ScreenshotPath[CFG_MaxString+1];
bool GamepadEnabled;
bool FirstStart;
int32_t DebugRec;
// if defined, the external file is used for debugrec writing. Otherwise read/check
int32_t DebugRecWrite;
// if defined, an external file is used for debugrec writing (replays only)
char DebugRecExternalFile[_MAX_PATH+1];
public:
static int GetLanguageSequence(const char *strSource, char *strTarget);

View File

@ -184,9 +184,8 @@ void C4GameControl::RequestRuntimeRecord()
fRecordNeeded = true;
// request through a synchronize-call
// currnetly do not request, but start record with next gamesync, so network runtime join can be debugged
#ifndef DEBUGREC
::Control.DoInput(CID_Synchronize, new C4ControlSynchronize(false, true), CDT_Queue);
#endif
if (Config.General.DebugRec)
::Control.DoInput(CID_Synchronize, new C4ControlSynchronize(false, true), CDT_Queue);
}
bool C4GameControl::IsRuntimeRecordPossible() const
@ -422,18 +421,19 @@ void C4GameControl::DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4Con
void C4GameControl::DbgRec(C4RecordChunkType eType, const uint8_t *pData, size_t iSize)
{
#ifdef DEBUGREC
if (DoNoDebugRec>0) return;
// record data
if (pRecord)
if (Config.General.DebugRec)
{
C4PktDebugRec dr(eType, StdBuf(pData, iSize));
pRecord->Rec(Game.FrameCounter, DecompileToBuf<StdCompilerBinWrite>(dr), eType);
if (DoNoDebugRec>0) return;
// record data
if (pRecord)
{
C4PktDebugRec dr(eType, StdBuf(pData, iSize));
pRecord->Rec(Game.FrameCounter, DecompileToBuf<StdCompilerBinWrite>(dr), eType);
}
// check against playback
if (pPlayback)
pPlayback->Check(eType, pData, iSize);
}
// check against playback
if (pPlayback)
pPlayback->Check(eType, pData, iSize);
#endif // DEBUGREC
}
C4ControlDeliveryType C4GameControl::DecideControlDelivery()

View File

@ -39,9 +39,7 @@
#include <algorithm>
#ifdef DEBUGREC
#include "C4Record.h"
#endif
/* C4PlayerControlDef */
@ -1073,12 +1071,13 @@ void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl *p
const C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(rItem.iControl);
if (pCtrlDef)
{
#ifdef DEBUGREC
if (pCtrlDef->IsSync())
if (Config.General.DebugRec)
{
AddDbgRec(RCT_PlrCom, &rItem.iControl, sizeof(rItem.iControl));
if (pCtrlDef->IsSync())
{
AddDbgRec(RCT_PlrCom, &rItem.iControl, sizeof(rItem.iControl));
}
}
#endif
if (ExecuteControl(rItem.iControl, pCtrl->IsReleaseControl(), pCtrl->GetExtraData(), rItem.iTriggerMode, false, fHandleDownStateOnly))
if (pCtrlDef->IsSync())
{

View File

@ -37,20 +37,13 @@
#define IMMEDIATEREC
//#define DEBUGREC_EXTFILE "DbgRec.ocb" // if defined, an external file is used for debugrec writing (replays only)
//#define DEBUGREC_EXTFILE_WRITE // if defined, the external file is used for debugrec writing. Otherwise read/check
#ifdef DEBUGREC
#ifdef DEBUGREC_EXTFILE
CStdFile DbgRecFile;
#endif
int DoNoDebugRec=0; // debugrec disable counter
void AddDbgRec(C4RecordChunkType eType, const void *pData, int iSize)
{
::Control.DbgRec(eType, (const uint8_t *) pData, iSize);
}
#endif
C4DebugRecOff::C4DebugRecOff() : fDoOff(true)
{
@ -216,9 +209,8 @@ bool C4Record::Stop(StdStrBuf *pRecordName, BYTE *pRecordSHA1)
LogRec.Close();
// pack group
#ifndef DEBUGREC
if (!C4Group_PackDirectory(sFilename.getData())) return false;
#endif
if (!Config.General.DebugRec)
if (!C4Group_PackDirectory(sFilename.getData())) return false;
// return record data
if (pRecordName)
@ -501,23 +493,27 @@ bool C4Playback::Open(C4Group &rGrp)
currChunk = chunks.begin();
Finished = false;
// external debugrec file
#if defined(DEBUGREC_EXTFILE) && defined(DEBUGREC)
#ifdef DEBUGREC_EXTFILE_WRITE
if (!DbgRecFile.Create(DEBUGREC_EXTFILE))
if (Config.General.DebugRecExternalFile[0] && Config.General.DebugRec)
{
LogFatal("DbgRec: Creation of external file \"" DEBUGREC_EXTFILE "\" failed!");
return false;
if (Config.General.DebugRecWrite)
{
if (!DbgRecFile.Create(Config.General.DebugRecExternalFile))
{
LogFatal(FormatString("DbgRec: Creation of external file \"%s\" failed!", Config.General.DebugRecExternalFile).getData());
return false;
}
else LogF("DbgRec: Writing to \"%s\"...", Config.General.DebugRecExternalFile);
}
else
{
if (!DbgRecFile.Open(Config.General.DebugRecExternalFile))
{
LogFatal(FormatString("DbgRec: Opening of external file \"%s\" failed!", Config.General.DebugRecExternalFile).getData());
return false;
}
else LogF("DbgRec: Checking against \"%s\"...", Config.General.DebugRecExternalFile);
}
}
else Log("DbgRec: Writing to \"" DEBUGREC_EXTFILE "\"...");
#else
if (!DbgRecFile.Open(DEBUGREC_EXTFILE))
{
LogFatal("DbgRec: Opening of external file \"" DEBUGREC_EXTFILE "\" failed!");
return false;
}
else Log("DbgRec: Checking against \"" DEBUGREC_EXTFILE "\"...");
#endif
#endif
// ok
return true;
}
@ -860,11 +856,12 @@ bool C4Playback::ExecuteControl(C4Control *pCtrl, int iFrame)
// still playbacking?
if (currChunk == chunks.end()) return false;
if (Finished) { Finish(); return false; }
#ifdef DEBUGREC
if (DebugRec.firstPkt())
DebugRecError("Debug rec overflow!");
DebugRec.Clear();
#endif
if (Config.General.DebugRec)
{
if (DebugRec.firstPkt())
DebugRecError("Debug rec overflow!");
DebugRec.Clear();
}
// return all control until this frame
while (currChunk != chunks.end() && currChunk->Frame <= iFrame)
{
@ -887,28 +884,22 @@ bool C4Playback::ExecuteControl(C4Control *pCtrl, int iFrame)
Finished=true;
break;
#ifdef DEBUGREC
default: // expect it to be debug rec
// append to debug rec buffer
if (currChunk->pDbg)
if (Config.General.DebugRec)
{
DebugRec.Add(CID_DebugRec, currChunk->pDbg);
// the debugrec buffer is now responsible for deleting the packet
currChunk->pDbg = NULL;
// append to debug rec buffer
if (currChunk->pDbg)
{
DebugRec.Add(CID_DebugRec, currChunk->pDbg);
// the debugrec buffer is now responsible for deleting the packet
currChunk->pDbg = NULL;
}
break;
}
break;
#endif
}
// next chunk
NextChunk();
}
// Debug log
#ifdef DEBUGREC
//sprintf(OSTR, "-- Frame %d:", Game.FrameCounter); Log(OSTR);
//char Indent[256+1]; strcpy(Indent, "");
//pCtrl->deb_print(Indent);
#endif
return true;
}
@ -937,13 +928,13 @@ void C4Playback::Clear()
playbackFile.Close();
sequentialBuffer.Clear();
fLoadSequential = false;
#ifdef DEBUGREC
C4IDPacket *pkt;
while (pkt = DebugRec.firstPkt()) DebugRec.Delete(pkt);
#ifdef DEBUGREC_EXTFILE
DbgRecFile.Close();
#endif
#endif
if (Config.General.DebugRec)
{
C4IDPacket *pkt;
while (pkt = DebugRec.firstPkt()) DebugRec.Delete(pkt);
if (Config.General.DebugRecExternalFile[0])
DbgRecFile.Close();
}
// done
Finished = true;
}
@ -1012,7 +1003,6 @@ StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf & RawData)
return r;
}
#ifdef DEBUGREC
void C4Playback::Check(C4RecordChunkType eType, const uint8_t *pData, int iSize)
{
// only if enabled
@ -1021,66 +1011,72 @@ void C4Playback::Check(C4RecordChunkType eType, const uint8_t *pData, int iSize)
C4PktDebugRec PktInReplay;
bool fHasPacketFromHead = false;
#ifdef DEBUGREC_EXTFILE
#ifdef DEBUGREC_EXTFILE_WRITE
// writing of external debugrec file
DbgRecFile.Write(&eType, sizeof eType);
int32_t iSize32 = iSize;
DbgRecFile.Write(&iSize32, sizeof iSize32);
DbgRecFile.Write(pData, iSize);
return;
#else
int32_t iSize32 = 0;
C4RecordChunkType eTypeRec = RCT_Undefined;
DbgRecFile.Read(&eTypeRec, sizeof eTypeRec);
DbgRecFile.Read(&iSize32, sizeof iSize32);
if (iSize32)
if (Config.General.DebugRecExternalFile[0])
{
StdBuf buf;
buf.SetSize(iSize32);
DbgRecFile.Read(buf.getMData(), iSize32);
PktInReplay = C4PktDebugRec(eTypeRec, buf);
}
#endif
#else
// check debug rec in list
C4IDPacket *pkt;
if (pkt = DebugRec.firstPkt())
{
// copy from list
PktInReplay = *static_cast<C4PktDebugRec *>(pkt->getPkt());
DebugRec.Delete(pkt);
if (Config.General.DebugRecWrite)
{
// writing of external debugrec file
DbgRecFile.Write(&eType, sizeof eType);
int32_t iSize32 = iSize;
DbgRecFile.Write(&iSize32, sizeof iSize32);
DbgRecFile.Write(pData, iSize);
return;
}
else
{
int32_t iSize32 = 0;
C4RecordChunkType eTypeRec = RCT_Undefined;
DbgRecFile.Read(&eTypeRec, sizeof eTypeRec);
DbgRecFile.Read(&iSize32, sizeof iSize32);
if (iSize32)
{
StdBuf buf;
buf.SetSize(iSize32);
DbgRecFile.Read(buf.getMData(), iSize32);
PktInReplay = C4PktDebugRec(eTypeRec, buf);
}
}
}
else
{
// special sync check skip...
while (currChunk != chunks.end() && currChunk->Type == RCT_CtrlPkt)
// check debug rec in list
C4IDPacket *pkt;
if (pkt = DebugRec.firstPkt())
{
C4IDPacket Packet(*currChunk->pPkt);
C4ControlPacket *pCtrlPck = static_cast<C4ControlPacket *>(Packet.getPkt());
assert(!pCtrlPck->Sync());
::Control.ExecControlPacket(Packet.getPktType(), pCtrlPck);
NextChunk();
// copy from list
PktInReplay = *static_cast<C4PktDebugRec *>(pkt->getPkt());
DebugRec.Delete(pkt);
}
// record end?
if (currChunk == chunks.end() || currChunk->Type == RCT_End || Finished)
else
{
Log("DebugRec end: All in sync!");
++DoNoDebugRec;
return;
}
// unpack directly from head
if (currChunk->Type != eType)
{
DebugRecError(FormatString("Playback type %x, this type %x", currChunk->Type, eType).getData());
return;
}
if (currChunk->pDbg)
PktInReplay = *currChunk->pDbg;
// special sync check skip...
while (currChunk != chunks.end() && currChunk->Type == RCT_CtrlPkt)
{
C4IDPacket Packet(*currChunk->pPkt);
C4ControlPacket *pCtrlPck = static_cast<C4ControlPacket *>(Packet.getPkt());
assert(!pCtrlPck->Sync());
::Control.ExecControlPacket(Packet.getPktType(), pCtrlPck);
NextChunk();
}
// record end?
if (currChunk == chunks.end() || currChunk->Type == RCT_End || Finished)
{
Log("DebugRec end: All in sync!");
++DoNoDebugRec;
return;
}
// unpack directly from head
if (currChunk->Type != eType)
{
DebugRecError(FormatString("Playback type %x, this type %x", currChunk->Type, eType).getData());
return;
}
if (currChunk->pDbg)
PktInReplay = *currChunk->pDbg;
fHasPacketFromHead = true;
fHasPacketFromHead = true;
}
}
#endif // DEBUGREC_EXTFILE
// record end?
if (PktInReplay.getType() == RCT_End)
{
@ -1121,7 +1117,6 @@ void C4Playback::DebugRecError(const char *szError)
LogF("Playback error: %s", szError);
BREAKPOINT_HERE;
}
#endif
bool C4Playback::StreamToRecord(const char *szStream, StdStrBuf *pRecordFile)
{

View File

@ -27,17 +27,11 @@ class C4Record;
#include "C4Group.h"
#include "C4Control.h"
#ifdef DEBUGREC
extern int DoNoDebugRec; // debugrec disable counter in C4Record.cpp
#define DEBUGREC_OFF ++DoNoDebugRec;
#define DEBUGREC_ON --DoNoDebugRec;
#else
#define DEBUGREC_OFF
#define DEBUGREC_ON
#endif
// turn off debugrecs in current block
class C4DebugRecOff
{
@ -100,9 +94,7 @@ enum C4RecordChunkType // record file chunk type
RCT_Undefined = 0xff
};
#ifdef DEBUGREC
void AddDbgRec(C4RecordChunkType eType, const void *pData=NULL, int iSize=0); // record debug stuff
#endif
#pragma pack(1)
@ -306,9 +298,7 @@ private:
StdBuf sequentialBuffer; // buffer to manage sequential reads
uint32_t iLastSequentialFrame; // frame number of last chunk read
void Finish(); // end playback
#ifdef DEBUGREC
C4PacketList DebugRec;
#endif
public:
C4Playback(); // constructor; init playback
~C4Playback(); // destructor; deinit playback
@ -324,11 +314,9 @@ public:
bool ExecuteControl(C4Control *pCtrl, int iFrame); // assign control
bool IsFinished() { return Finished; }
void Clear();
#ifdef DEBUGREC
void Check(C4RecordChunkType eType, const uint8_t *pData, int iSize); // compare with debugrec
void DebugRecError(const char *szError);
#endif
static bool StreamToRecord(const char *szStream, StdStrBuf *pRecord);
};
#endif
#endif

View File

@ -227,6 +227,9 @@ void C4Application::ParseCommandLine(int argc, char * argv[])
{"league", no_argument, &Config.Network.LeagueServerSignUp, 1},
{"nosignup", no_argument, &Config.Network.MasterServerSignUp, 0},
{"signup", no_argument, &Config.Network.MasterServerSignUp, 1},
{"debugrecread", required_argument, 0, 'K'},
{"debugrecwrite", required_argument, 0, 'w'},
{"client", required_argument, 0, 'c'},
{"host", no_argument, 0, 'h'},
@ -289,6 +292,28 @@ void C4Application::ParseCommandLine(int argc, char * argv[])
Game.NetworkActive = true;
SCopy(optarg, Game.DirectJoinAddress, _MAX_PATH);
break;
case 'K':
if (optarg && optarg[0])
{
LogF("Reading from DebugRec file '%s'", optarg);
SCopy(optarg, Config.General.DebugRecExternalFile, _MAX_PATH);
}
else
Log("Reading DebugRec from CtrlRec file in scenario record");
Config.General.DebugRec = 1;
Config.General.DebugRecWrite = 0;
break;
case 'w':
if (optarg && optarg[0])
{
LogF("Writing to DebugRec file '%s'", optarg);
SCopy(optarg, Config.General.DebugRecExternalFile, _MAX_PATH);
}
else
Log("Writing DebugRec to CtrlRec file in scenario record");
Config.General.DebugRec = 1;
Config.General.DebugRecWrite = 1;
break;
case 'r': Game.Record = true; break;
case 'n': Game.NetworkActive = true; break;
case 'N': Game.NetworkActive = false; break;

View File

@ -700,13 +700,8 @@ C4ST_NEW(MessagesStat, "C4Game::Execute Messages.Execute")
#define EXEC_S(Expressions, Stat) \
{ C4ST_START(Stat) Expressions C4ST_STOP(Stat) }
#ifdef DEBUGREC
#define EXEC_S_DR(Expressions, Stat, DebugRecName) { AddDbgRec(RCT_Block, DebugRecName, 6); EXEC_S(Expressions, Stat) }
#define EXEC_DR(Expressions, DebugRecName) { AddDbgRec(RCT_Block, DebugRecName, 6); Expressions }
#else
#define EXEC_S_DR(Expressions, Stat, DebugRecName) EXEC_S(Expressions, Stat)
#define EXEC_DR(Expressions, DebugRecName) Expressions
#endif
#define EXEC_S_DR(Expressions, Stat, DebugRecName) { if (Config.General.DebugRec) AddDbgRec(RCT_Block, DebugRecName, 6); EXEC_S(Expressions, Stat) }
#define EXEC_DR(Expressions, DebugRecName) { if (Config.General.DebugRec) AddDbgRec(RCT_Block, DebugRecName, 6); Expressions }
bool C4Game::Execute() // Returns true if the game is over
{
@ -725,9 +720,8 @@ bool C4Game::Execute() // Returns true if the game is over
// Halt
if (HaltCount) return false;
#ifdef DEBUGREC
Landscape.DoRelights();
#endif
if (Config.General.DebugRec)
Landscape.DoRelights();
// Execute the control
Control.Execute();
@ -736,10 +730,9 @@ bool C4Game::Execute() // Returns true if the game is over
// Ticks
EXEC_DR( Ticks(); , "Ticks")
#ifdef DEBUGREC
// debugrec
AddDbgRec(RCT_DbgFrame, &FrameCounter, sizeof(int32_t));
#endif
if (Config.General.DebugRec)
// debugrec
AddDbgRec(RCT_DbgFrame, &FrameCounter, sizeof(int32_t));
// Game
@ -777,11 +770,11 @@ bool C4Game::Execute() // Returns true if the game is over
C4ST_RESETPART
}
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "eGame", 6);
Landscape.DoRelights();
#endif
if (Config.General.DebugRec)
{
AddDbgRec(RCT_Block, "eGame", 6);
Landscape.DoRelights();
}
return true;
}
@ -1004,14 +997,15 @@ C4Object* C4Game::NewObject( C4PropList *pDef, C4Object *pCreator,
{
// Safety
if (!pDef) return NULL;
#ifdef DEBUGREC
C4RCCreateObj rc;
memset(&rc, '\0', sizeof(rc));
strncpy(rc.id, pDef->GetName(), 32+1);
rc.oei=C4PropListNumbered::GetEnumerationIndex()+1;
rc.x=iX; rc.y=iY; rc.ownr=iOwner;
AddDbgRec(RCT_CrObj, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCCreateObj rc;
memset(&rc, '\0', sizeof(rc));
strncpy(rc.id, pDef->GetName(), 32+1);
rc.oei=C4PropListNumbered::GetEnumerationIndex()+1;
rc.x=iX; rc.y=iY; rc.ownr=iOwner;
AddDbgRec(RCT_CrObj, &rc, sizeof(rc));
}
// Create object
C4Object *pObj;
if (!(pObj=new C4Object)) return NULL;
@ -1319,9 +1313,8 @@ void C4Game::ObjectRemovalCheck() // Every ::Game.iTick255 by ExecObjects
void C4Game::ExecObjects() // Every Tick1 by Execute
{
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "ObjEx", 6);
#endif
if (Config.General.DebugRec)
AddDbgRec(RCT_Block, "ObjEx", 6);
// Execute objects - reverse order to ensure
C4Object *cObj; C4ObjectLink *clnk;
@ -1333,9 +1326,8 @@ void C4Game::ExecObjects() // Every Tick1 by Execute
// Status reset: process removal delay
if (cObj->RemovalDelay>0) cObj->RemovalDelay--;
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "ObjCC", 6);
#endif
if (Config.General.DebugRec)
AddDbgRec(RCT_Block, "ObjCC", 6);
// Can savely reset object marker here
Objects.LastUsedMarker = 0;
@ -1343,9 +1335,8 @@ void C4Game::ExecObjects() // Every Tick1 by Execute
// Cross check objects
Objects.CrossCheck();
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "ObjRs", 6);
#endif
if (Config.General.DebugRec)
AddDbgRec(RCT_Block, "ObjRs", 6);
// Resort
if (fResortAnyObject)
@ -1354,9 +1345,8 @@ void C4Game::ExecObjects() // Every Tick1 by Execute
Objects.ResortUnsorted();
}
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "ObjRm", 6);
#endif
if (Config.General.DebugRec)
AddDbgRec(RCT_Block, "ObjRm", 6);
// Removal
if (!::Game.iTick255) ObjectRemovalCheck();

View File

@ -333,20 +333,35 @@ static bool FnSmoke(C4PropList * _this, long tx, long ty, long level, long dwClr
return true;
}
static bool FnInsertMaterial(C4PropList * _this, long mat, long x, long y, long vx, long vy, C4PropList *insert_position, bool query_only)
static bool FnInsertMaterial(C4PropList * _this, long mat, long x, long y, long vx, long vy, C4PropList *insert_position)
{
if (Object(_this)) { x+=Object(_this)->GetX(); y+=Object(_this)->GetY(); }
int32_t insert_x=x, insert_y=y;
if (!::Landscape.InsertMaterial(mat,&insert_x,&insert_y,vx,vy,query_only)) return false;
// output insertion position if desired
if (insert_position && !insert_position->IsFrozen())
{
insert_position->SetProperty(P_X, C4VInt(insert_x));
insert_position->SetProperty(P_Y, C4VInt(insert_y));
if (!::Landscape.InsertMaterial(mat,&insert_x,&insert_y,vx,vy)) return false;
// output insertion position if desired
if (insert_position && !insert_position->IsFrozen())
{
insert_position->SetProperty(P_X, C4VInt(insert_x));
insert_position->SetProperty(P_Y, C4VInt(insert_y));
}
return true;
}
static bool FnCanInsertMaterial(C4PropList * _this, long mat, long x, long y, C4PropList *insert_position)
{
if (Object(_this)) { x+=Object(_this)->GetX(); y+=Object(_this)->GetY(); }
int32_t insert_x=x, insert_y=y;
if (!::Landscape.InsertMaterial(mat,&insert_x,&insert_y,0,0,true)) return false;
// output insertion position if desired
if (insert_position && !insert_position->IsFrozen())
{
insert_position->SetProperty(P_X, C4VInt(insert_x));
insert_position->SetProperty(P_Y, C4VInt(insert_y));
}
return true;
}
static long FnGetMaterialCount(C4PropList * _this, long iMaterial, bool fReal)
{
if (!MatValid(iMaterial)) return -1;
@ -2411,6 +2426,7 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "Material", FnMaterial);
AddFunc(pEngine, "BlastFree", FnBlastFree);
AddFunc(pEngine, "InsertMaterial", FnInsertMaterial);
AddFunc(pEngine, "CanInsertMaterial", FnCanInsertMaterial);
AddFunc(pEngine, "LandscapeWidth", FnLandscapeWidth);
AddFunc(pEngine, "LandscapeHeight", FnLandscapeHeight);
AddFunc(pEngine, "SetSeason", FnSetSeason);

View File

@ -404,13 +404,14 @@ bool C4Menu::AddItem(C4MenuItem *pNew, const char *szCaption, const char *szComm
C4ID idID, const char *szCommand2, bool fOwnValue, int32_t iValue, bool fIsSelectable)
{
#ifdef DEBUGREC_MENU
if (pObject)
{
C4RCMenuAdd rc = { pObject ? pObject->Number : -1, iCount, idID, fOwnValue, iValue, fIsSelectable };
AddDbgRec(RCT_MenuAdd, &rc, sizeof(C4RCMenuAdd));
if (szCommand) AddDbgRec(RCT_MenuAddC, szCommand, strlen(szCommand)+1);
if (szCommand2) AddDbgRec(RCT_MenuAddC, szCommand2, strlen(szCommand2)+1);
}
if (Config.General.DebugRec)
if (pObject)
{
C4RCMenuAdd rc = { pObject ? pObject->Number : -1, iCount, idID, fOwnValue, iValue, fIsSelectable };
AddDbgRec(RCT_MenuAdd, &rc, sizeof(C4RCMenuAdd));
if (szCommand) AddDbgRec(RCT_MenuAddC, szCommand, strlen(szCommand)+1);
if (szCommand2) AddDbgRec(RCT_MenuAddC, szCommand2, strlen(szCommand2)+1);
}
#endif
// Add it to the list
pClientWindow->AddElement(pNew);

View File

@ -135,7 +135,8 @@ void C4Landscape::ExecuteScan()
return;
#ifdef DEBUGREC_MATSCAN
AddDbgRec(RCT_MatScan, &ScanX, sizeof(ScanX));
if (Config.General.DebugRec)
AddDbgRec(RCT_MatScan, &ScanX, sizeof(ScanX));
#endif
for (int32_t cnt=0; cnt<ScanSpeed; cnt++)
@ -192,8 +193,11 @@ int32_t C4Landscape::DoScan(int32_t cx, int32_t cy, int32_t mat, int32_t dir)
int32_t mconv = ::MaterialMap.Map[mat].TempConvStrength,
mconvs = mconv;
#ifdef DEBUGREC_MATSCAN
C4RCMatScan rc = { cx, cy, mat, conv_to, dir, mconvs };
AddDbgRec(RCT_MatScanDo, &rc, sizeof(C4RCMatScan));
if (Config.General.DebugRec)
{
C4RCMatScan rc = { cx, cy, mat, conv_to, dir, mconvs };
AddDbgRec(RCT_MatScanDo, &rc, sizeof(C4RCMatScan));
}
#endif
int32_t ydir = (dir == 0 ? +1 : -1), cy2;
#ifdef PRETTY_TEMP_CONV
@ -634,11 +638,12 @@ bool C4Landscape::ClearPix(int32_t tx, int32_t ty)
}
bool C4Landscape::SetPix(int32_t x, int32_t y, BYTE npix)
{
#ifdef DEBUGREC
C4RCSetPix rc;
rc.x=x; rc.y=y; rc.clr=npix;
AddDbgRec(RCT_SetPix, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCSetPix rc;
rc.x=x; rc.y=y; rc.clr=npix;
AddDbgRec(RCT_SetPix, &rc, sizeof(rc));
}
// check bounds
if (x < 0 || y < 0 || x >= Width || y >= Height)
return false;
@ -662,11 +667,12 @@ bool C4Landscape::SetPix(int32_t x, int32_t y, BYTE npix)
bool C4Landscape::_SetPix(int32_t x, int32_t y, BYTE npix)
{
#ifdef DEBUGREC
C4RCSetPix rc;
rc.x=x; rc.y=y; rc.clr=npix;
AddDbgRec(RCT_SetPix, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCSetPix rc;
rc.x=x; rc.y=y; rc.clr=npix;
AddDbgRec(RCT_SetPix, &rc, sizeof(rc));
}
assert(x >= 0 && y >= 0 && x < Width && y < Height);
// get and check pixel
BYTE opix = _GetPix(x, y);
@ -843,8 +849,13 @@ bool C4Landscape::InsertMaterial(int32_t mat, int32_t *tx, int32_t *ty, int32_t
if (GetDensity(*tx,*ty+1)<mdens)
{ if (!query_only) ::PXS.Create(mat,itofix(*tx),itofix(*ty),C4REAL10(vx),C4REAL10(vy)); return true; }
// Insertion OK here
if (query_only) return true;
if (query_only)
{
// since tx and ty changed, we need to re-check the bounds here
// if we really inserted it, the check is made again in InsertDeadMaterial
if (!Inside<int32_t>(*tx,0,Width-1) || !Inside<int32_t>(*ty,0,Height)) return false;
return true;
}
// Try reaction with material below and at insertion position
C4MaterialReaction *pReact; int32_t tmat;
@ -1223,10 +1234,11 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
return true;
}
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "|---MAP---|", 12);
AddDbgRec(RCT_Map, sfcMap->Bits, sfcMap->Pitch*sfcMap->Hgt);
#endif
if (Config.General.DebugRec)
{
AddDbgRec(RCT_Block, "|---MAP---|", 12);
AddDbgRec(RCT_Map, sfcMap->Bits, sfcMap->Pitch*sfcMap->Hgt);
}
// Store map size and calculate map zoom
int iWdt, iHgt;
@ -1301,10 +1313,11 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
Game.SetInitProgress(84);
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "|---LANDSCAPE---|", 18);
AddDbgRec(RCT_Map, Surface8->Bits, Surface8->Pitch*Surface8->Hgt);
#endif
if (Config.General.DebugRec)
{
AddDbgRec(RCT_Block, "|---LANDSCAPE---|", 18);
AddDbgRec(RCT_Map, Surface8->Bits, Surface8->Pitch*Surface8->Hgt);
}
// Create renderer
pLandscapeRender = NULL;
@ -1327,10 +1340,11 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
pLandscapeRender->Update(C4Rect(0, 0, Width, Height), this);
Game.SetInitProgress(87);
}
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "|---LS---|", 11);
AddDbgRec(RCT_Ls, Surface8->Bits, Surface8->Pitch*Surface8->Hgt);
#endif
if (Config.General.DebugRec)
{
AddDbgRec(RCT_Block, "|---LS---|", 11);
AddDbgRec(RCT_Ls, Surface8->Bits, Surface8->Pitch*Surface8->Hgt);
}
// Create pixel count array

View File

@ -516,11 +516,12 @@ bool C4MCOverlay::CheckMask(int32_t iX, int32_t iY)
{
// bounds match?
if (!LooseBounds) if (iX<X || iY<Y || iX>=X+Wdt || iY>=Y+Hgt) return false;
#ifdef DEBUGREC
C4RCTrf rc;
rc.x=iX; rc.y=iY; rc.Rotate=Rotate; rc.Turbulence=Turbulence;
AddDbgRec(RCT_MCT1, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCTrf rc;
rc.x=iX; rc.y=iY; rc.Rotate=Rotate; rc.Turbulence=Turbulence;
AddDbgRec(RCT_MCT1, &rc, sizeof(rc));
}
C4Real dX=itofix(iX); C4Real dY=itofix(iY);
// apply turbulence
if (Turbulence)
@ -558,11 +559,12 @@ bool C4MCOverlay::CheckMask(int32_t iX, int32_t iY)
{ iX=fixtoi(dX, ZoomX); iY=fixtoi(dY, ZoomY); }
else
{ iX*=ZoomX; iY*=ZoomY; }
#ifdef DEBUGREC
C4RCPos rc2;
rc2.x=iX; rc2.y=iY;
AddDbgRec(RCT_MCT2, &rc2, sizeof(rc2));
#endif
if (Config.General.DebugRec)
{
C4RCPos rc2;
rc2.x=iX; rc2.y=iY;
AddDbgRec(RCT_MCT2, &rc2, sizeof(rc2));
}
// apply offset
iX-=OffX*ZoomX; iY-=OffY*ZoomY;
// check bounds, if loose

View File

@ -75,11 +75,12 @@ void C4MassMoverSet::Execute()
bool C4MassMoverSet::Create(int32_t x, int32_t y, bool fExecute)
{
if (Count == C4MassMoverChunk) return false;
#ifdef DEBUGREC
C4RCMassMover rc;
rc.x=x; rc.y=y;
AddDbgRec(RCT_MMC, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCMassMover rc;
rc.x=x; rc.y=y;
AddDbgRec(RCT_MMC, &rc, sizeof(rc));
}
int32_t cptr=CreatePtr;
do
{
@ -118,11 +119,12 @@ bool C4MassMover::Init(int32_t tx, int32_t ty)
void C4MassMover::Cease()
{
#ifdef DEBUGREC
C4RCMassMover rc;
rc.x=x; rc.y=y;
AddDbgRec(RCT_MMD, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCMassMover rc;
rc.x=x; rc.y=y;
AddDbgRec(RCT_MMD, &rc, sizeof(rc));
}
::MassMover.Count--;
Mat=MNone;
}

View File

@ -38,6 +38,7 @@ static const C4Real WindDrift_Factor = itofix(1, 800);
void C4PXS::Execute()
{
#ifdef DEBUGREC_PXS
if (Config.General.DebugRec)
{
C4RCExecPXS rc;
rc.x=x; rc.y=y; rc.iMat=Mat;
@ -127,6 +128,7 @@ void C4PXS::Execute()
// No contact? Free movement
x=ctcox; y=ctcoy;
#ifdef DEBUGREC_PXS
if (Config.General.DebugRec)
{
C4RCExecPXS rc;
rc.x=x; rc.y=y; rc.iMat=Mat;
@ -140,10 +142,13 @@ void C4PXS::Execute()
void C4PXS::Deactivate()
{
#ifdef DEBUGREC_PXS
C4RCExecPXS rc;
rc.x=x; rc.y=y; rc.iMat=Mat;
rc.pos = 2;
AddDbgRec(RCT_ExecPXS, &rc, sizeof(rc));
if (Config.General.DebugRec)
{
C4RCExecPXS rc;
rc.x=x; rc.y=y; rc.iMat=Mat;
rc.pos = 2;
AddDbgRec(RCT_ExecPXS, &rc, sizeof(rc));
}
#endif
Mat=MNone;
::PXS.Delete(this);

View File

@ -26,23 +26,30 @@
int RandomCount = 0;
unsigned int RandomHold = 0;
#ifdef DEBUGREC
int Random(int iRange)
{
// next pseudorandom value
RandomCount++;
C4RCRandom rc;
rc.Cnt=RandomCount;
rc.Range=iRange;
if (iRange==0)
rc.Val=0;
if (Config.General.DebugRec)
{
// next pseudorandom value
RandomCount++;
C4RCRandom rc;
rc.Cnt=RandomCount;
rc.Range=iRange;
if (iRange==0)
rc.Val=0;
else
{
RandomHold = ((uint64_t)RandomHold * 16807) % 2147483647;
rc.Val = RandomHold % iRange;
}
AddDbgRec(RCT_Random, &rc, sizeof(rc));
return rc.Val;
}
else
{
RandomCount++;
if (iRange==0) return 0;
RandomHold = ((uint64_t)RandomHold * 16807) % 2147483647;
rc.Val = RandomHold % iRange;
return RandomHold % iRange;
}
AddDbgRec(RCT_Random, &rc, sizeof(rc));
return rc.Val;
}
#endif

View File

@ -37,17 +37,7 @@ inline void FixedRandom(DWORD dwSeed)
RandomCount=0;
}
#ifdef DEBUGREC
int Random(int iRange);
#else
inline int Random(int iRange)
{
RandomCount++;
if (iRange==0) return 0;
RandomHold = ((uint64_t)RandomHold * 16807) % 2147483647;
return RandomHold % iRange;
}
#endif
inline unsigned int SeededRandom(unsigned int iSeed, unsigned int iRange)
{

View File

@ -153,11 +153,27 @@ namespace
ValueProviderAdapt mkValueProviderAdapt(StdMeshInstance::ValueProvider** ValueProvider) { return ValueProviderAdapt(ValueProvider); }
// Serialize a bone index by name with StdCompiler
struct TransformAdapt
void CompileFloat(StdCompiler* pComp, float& f)
{
// TODO: Teach StdCompiler how to handle float
if(pComp->isCompiler())
{
C4Real r;
pComp->Value(r);
f = fixtof(r);
}
else
{
C4Real r = ftofix(f);
pComp->Value(r);
}
}
// Serialize a transformation matrix by name with StdCompiler
struct MatrixAdapt
{
StdMeshMatrix& Matrix;
TransformAdapt(StdMeshMatrix& matrix): Matrix(matrix) {}
MatrixAdapt(StdMeshMatrix& matrix): Matrix(matrix) {}
void CompileFunc(StdCompiler* pComp)
{
@ -167,30 +183,42 @@ namespace
for(unsigned int j = 0; j < 4; ++j)
{
if(i != 0 || j != 0) pComp->Separator();
// TODO: Teach StdCompiler how to handle float
// pComp->Value(Matrix(i, j));
if(pComp->isCompiler())
{
C4Real f;
pComp->Value(f);
Matrix(i,j) = fixtof(f);
}
else
{
C4Real f = ftofix(Matrix(i,j));
pComp->Value(f);
}
CompileFloat(pComp, Matrix(i, j));
}
}
pComp->Separator(StdCompiler::SEP_END);
}
ALLOW_TEMP_TO_REF(TransformAdapt)
ALLOW_TEMP_TO_REF(MatrixAdapt)
};
TransformAdapt mkTransformAdapt(StdMeshMatrix& Matrix) { return TransformAdapt(Matrix); }
struct TransformAdapt
{
StdMeshTransformation& Trans;
TransformAdapt(StdMeshTransformation& trans): Trans(trans) {}
void CompileFunc(StdCompiler* pComp)
{
pComp->Separator(StdCompiler::SEP_START);
CompileFloat(pComp, Trans.translate.x);
CompileFloat(pComp, Trans.translate.y);
CompileFloat(pComp, Trans.translate.z);
CompileFloat(pComp, Trans.rotate.w);
CompileFloat(pComp, Trans.rotate.x);
CompileFloat(pComp, Trans.rotate.y);
CompileFloat(pComp, Trans.rotate.z);
CompileFloat(pComp, Trans.scale.x);
CompileFloat(pComp, Trans.scale.y);
CompileFloat(pComp, Trans.scale.z);
pComp->Separator(StdCompiler::SEP_END);
}
ALLOW_TEMP_TO_REF(TransformAdapt);
};
MatrixAdapt mkMatrixAdapt(StdMeshMatrix& Matrix) { return MatrixAdapt(Matrix); }
TransformAdapt mkTransformAdapt(StdMeshTransformation& Trans) { return TransformAdapt(Trans); }
// Reset all animation list entries corresponding to node or its children
void ClearAnimationListRecursively(std::vector<StdMeshInstance::AnimationNode*>& list, StdMeshInstance::AnimationNode* node)
@ -530,6 +558,13 @@ StdMeshInstance::AnimationNode::AnimationNode(const StdMeshAnimation* animation,
Leaf.Position = position;
}
StdMeshInstance::AnimationNode::AnimationNode(const StdMeshBone* bone, const StdMeshTransformation& trans):
Type(CustomNode), Parent(NULL)
{
Custom.BoneIndex = bone->Index;
Custom.Transformation = new StdMeshTransformation(trans);
}
StdMeshInstance::AnimationNode::AnimationNode(AnimationNode* child_left, AnimationNode* child_right, ValueProvider* weight):
Type(LinearInterpolationNode), Parent(NULL)
{
@ -545,6 +580,9 @@ StdMeshInstance::AnimationNode::~AnimationNode()
case LeafNode:
delete Leaf.Position;
break;
case CustomNode:
delete Custom.Transformation;
break;
case LinearInterpolationNode:
delete LinearInterpolation.ChildLeft;
delete LinearInterpolation.ChildRight;
@ -566,6 +604,12 @@ bool StdMeshInstance::AnimationNode::GetBoneTransform(unsigned int bone, StdMesh
if (!track) return false;
transformation = track->GetTransformAt(fixtof(Leaf.Position->Value));
return true;
case CustomNode:
if(bone == Custom.BoneIndex)
transformation = *Custom.Transformation;
else
return false;
return true;
case LinearInterpolationNode:
if (!LinearInterpolation.ChildLeft->GetBoneTransform(bone, transformation))
return LinearInterpolation.ChildRight->GetBoneTransform(bone, transformation);
@ -585,6 +629,7 @@ void StdMeshInstance::AnimationNode::CompileFunc(StdCompiler* pComp, const StdMe
static const StdEnumEntry<NodeType> NodeTypes[] =
{
{ "Leaf", LeafNode },
{ "Custom", CustomNode },
{ "LinearInterpolation", LinearInterpolationNode },
{ NULL, static_cast<NodeType>(0) }
@ -611,6 +656,22 @@ void StdMeshInstance::AnimationNode::CompileFunc(StdCompiler* pComp, const StdMe
pComp->Value(mkNamingAdapt(mkValueProviderAdapt(&Leaf.Position), "Position"));
break;
case CustomNode:
if(pComp->isCompiler())
{
StdCopyStrBuf bone_name;
pComp->Value(mkNamingAdapt(toC4CStrBuf(bone_name), "Bone"));
const StdMeshBone* bone = Mesh->GetBoneByName(bone_name);
if(!bone) pComp->excCorrupt("No such bone: \"%s\"", bone_name.getData());
Custom.BoneIndex = bone->Index;
}
else
{
pComp->Value(mkNamingAdapt(mkParAdapt(mkDecompileAdapt(Mesh->GetBone(Custom.BoneIndex).Name), StdCompiler::RCT_All), "Bone"));
}
pComp->Value(mkNamingAdapt(mkTransformAdapt(*Custom.Transformation), "Transformation"));
break;
case LinearInterpolationNode:
pComp->Value(mkParAdapt(mkNamingPtrAdapt(LinearInterpolation.ChildLeft, "ChildLeft"), Mesh));
pComp->Value(mkParAdapt(mkNamingPtrAdapt(LinearInterpolation.ChildRight, "ChildRight"), Mesh));
@ -639,14 +700,38 @@ void StdMeshInstance::AnimationNode::DenumeratePointers()
case LeafNode:
value_provider = dynamic_cast<SerializableValueProvider*>(Leaf.Position);
break;
case CustomNode:
value_provider = NULL;
break;
case LinearInterpolationNode:
value_provider = dynamic_cast<SerializableValueProvider*>(LinearInterpolation.Weight);
// non-recursive, StdMeshInstance::DenumeratePointers walks over all nodes
break;
}
if(value_provider) value_provider->DenumeratePointers();
}
void StdMeshInstance::AnimationNode::ClearPointers(class C4Object* pObj)
{
SerializableValueProvider* value_provider = NULL;
switch(Type)
{
case LeafNode:
value_provider = dynamic_cast<SerializableValueProvider*>(Leaf.Position);
break;
case CustomNode:
value_provider = NULL;
break;
case LinearInterpolationNode:
value_provider = dynamic_cast<SerializableValueProvider*>(LinearInterpolation.Weight);
// non-recursive, StdMeshInstance::ClearPointers walks over all nodes
break;
}
if(value_provider) value_provider->ClearPointers(pObj);
}
StdMeshInstance::AttachedMesh::AttachedMesh():
Number(0), Parent(NULL), Child(NULL), OwnChild(true), ChildDenumerator(NULL), ParentBone(0), ChildBone(0), FinalTransformDirty(false)
{
@ -711,8 +796,8 @@ void StdMeshInstance::AttachedMesh::CompileFunc(StdCompiler* pComp, DenumeratorF
pComp->Value(mkNamingAdapt(Number, "Number"));
pComp->Value(mkNamingAdapt(ParentBone, "ParentBone")); // TODO: Save as string
pComp->Value(mkNamingAdapt(ChildBone, "ChildBone")); // TODO: Save as string (note we can only resolve this in DenumeratePointers then!)
pComp->Value(mkNamingAdapt(mkTransformAdapt(AttachTrans), "AttachTransformation"));
pComp->Value(mkNamingAdapt(mkMatrixAdapt(AttachTrans), "AttachTransformation"));
uint8_t dwSyncFlags = static_cast<uint8_t>(Flags);
pComp->Value(mkNamingAdapt(mkBitfieldAdapt(dwSyncFlags, AM_Entries), "Flags", 0u));
if(pComp->isCompiler()) Flags = dwSyncFlags;
@ -725,6 +810,11 @@ void StdMeshInstance::AttachedMesh::DenumeratePointers()
ChildDenumerator->DenumeratePointers(this);
}
bool StdMeshInstance::AttachedMesh::ClearPointers(class C4Object* pObj)
{
return ChildDenumerator->ClearPointers(pObj);
}
StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
Mesh(&mesh), SharedVertices(mesh.GetSharedVertices().size()), Completion(completion),
BoneTransforms(Mesh->GetNumBones(), StdMeshMatrix::Identity()),
@ -806,70 +896,16 @@ StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdStrBuf&
StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdMeshAnimation& animation, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight)
{
// Default
if (!sibling) sibling = GetRootAnimationForSlot(slot);
assert(!sibling || sibling->Slot == slot);
// Find two subsequent numbers in case we need to create two nodes, so
// script can deduce the second node.
unsigned int Number1, Number2;
for (Number1 = 0; Number1 < AnimationNodes.size(); ++Number1)
if (AnimationNodes[Number1] == NULL && (!sibling || Number1+1 == AnimationNodes.size() || AnimationNodes[Number1+1] == NULL))
break;
/* for(Number2 = Number1+1; Number2 < AnimationNodes.size(); ++Number2)
if(AnimationNodes[Number2] == NULL)
break;*/
Number2 = Number1 + 1;
position->Value = BoundBy(position->Value, Fix0, ftofix(animation.Length));
weight->Value = BoundBy(weight->Value, Fix0, itofix(1));
if (Number1 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) NULL);
if (sibling && Number2 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) NULL);
AnimationNode* child = new AnimationNode(&animation, position);
AnimationNodes[Number1] = child;
child->Number = Number1;
child->Slot = slot;
InsertAnimationNode(child, slot, sibling, weight);
return child;
}
if (sibling)
{
AnimationNode* parent = new AnimationNode(child, sibling, weight);
AnimationNodes[Number2] = parent;
parent->Number = Number2;
parent->Slot = slot;
child->Parent = parent;
parent->Parent = sibling->Parent;
parent->LinearInterpolation.ChildLeft = sibling;
parent->LinearInterpolation.ChildRight = child;
if (sibling->Parent)
{
if (sibling->Parent->LinearInterpolation.ChildLeft == sibling)
sibling->Parent->LinearInterpolation.ChildLeft = parent;
else
sibling->Parent->LinearInterpolation.ChildRight = parent;
}
else
{
// set new parent
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, false);
// slot must not be empty, since sibling uses same slot
assert(iter != AnimationStack.end() && *iter != NULL);
*iter = parent;
}
sibling->Parent = parent;
}
else
{
delete weight;
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, true);
assert(!*iter); // we have a sibling if slot is not empty
*iter = child;
}
BoneTransformsDirty = true;
StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdMeshBone* bone, const StdMeshTransformation& trans, int slot, AnimationNode* sibling, ValueProvider* weight)
{
AnimationNode* child = new AnimationNode(bone, trans);
InsertAnimationNode(child, slot, sibling, weight);
return child;
}
@ -954,6 +990,13 @@ void StdMeshInstance::SetAnimationPosition(AnimationNode* node, ValueProvider* p
BoneTransformsDirty = true;
}
void StdMeshInstance::SetAnimationBoneTransform(AnimationNode* node, const StdMeshTransformation& trans)
{
assert(node->GetType() == AnimationNode::CustomNode);
*node->Custom.Transformation = trans;
BoneTransformsDirty = true;
}
void StdMeshInstance::SetAnimationWeight(AnimationNode* node, ValueProvider* weight)
{
assert(node->GetType() == AnimationNode::LinearInterpolationNode);
@ -1294,6 +1337,21 @@ void StdMeshInstance::DenumeratePointers()
AttachChildren[i]->DenumeratePointers();
}
void StdMeshInstance::ClearPointers(class C4Object* pObj)
{
for(unsigned int i = 0; i < AnimationNodes.size(); ++i)
if(AnimationNodes[i])
AnimationNodes[i]->ClearPointers(pObj);
std::vector<unsigned int> Removal;
for(unsigned int i = 0; i < AttachChildren.size(); ++i)
if(!AttachChildren[i]->ClearPointers(pObj))
Removal.push_back(AttachChildren[i]->Number);
for(unsigned int i = 0; i < Removal.size(); ++i)
DetachMesh(Removal[i]);
}
StdMeshInstance::AnimationNodeList::iterator StdMeshInstance::GetStackIterForSlot(int slot, bool create)
{
// TODO: bsearch
@ -1318,6 +1376,72 @@ StdMeshInstance::AnimationNodeList::iterator StdMeshInstance::GetStackIterForSlo
return AnimationStack.insert(AnimationStack.end(), NULL);
}
void StdMeshInstance::InsertAnimationNode(AnimationNode* node, int slot, AnimationNode* sibling, ValueProvider* weight)
{
// Default
if (!sibling) sibling = GetRootAnimationForSlot(slot);
assert(!sibling || sibling->Slot == slot);
// Find two subsequent numbers in case we need to create two nodes, so
// script can deduce the second node.
unsigned int Number1, Number2;
for (Number1 = 0; Number1 < AnimationNodes.size(); ++Number1)
if (AnimationNodes[Number1] == NULL && (!sibling || Number1+1 == AnimationNodes.size() || AnimationNodes[Number1+1] == NULL))
break;
/* for(Number2 = Number1+1; Number2 < AnimationNodes.size(); ++Number2)
if(AnimationNodes[Number2] == NULL)
break;*/
Number2 = Number1 + 1;
weight->Value = BoundBy(weight->Value, Fix0, itofix(1));
if (Number1 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) NULL);
if (sibling && Number2 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) NULL);
AnimationNodes[Number1] = node;
node->Number = Number1;
node->Slot = slot;
if (sibling)
{
AnimationNode* parent = new AnimationNode(node, sibling, weight);
AnimationNodes[Number2] = parent;
parent->Number = Number2;
parent->Slot = slot;
node->Parent = parent;
parent->Parent = sibling->Parent;
parent->LinearInterpolation.ChildLeft = sibling;
parent->LinearInterpolation.ChildRight = node;
if (sibling->Parent)
{
if (sibling->Parent->LinearInterpolation.ChildLeft == sibling)
sibling->Parent->LinearInterpolation.ChildLeft = parent;
else
sibling->Parent->LinearInterpolation.ChildRight = parent;
}
else
{
// set new parent
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, false);
// slot must not be empty, since sibling uses same slot
assert(iter != AnimationStack.end() && *iter != NULL);
*iter = parent;
}
sibling->Parent = parent;
}
else
{
delete weight;
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, true);
assert(!*iter); // we have a sibling if slot is not empty
*iter = node;
}
BoneTransformsDirty = true;
}
bool StdMeshInstance::ExecuteAnimationNode(AnimationNode* node)
{
ValueProvider* provider = NULL;
@ -1331,6 +1455,9 @@ bool StdMeshInstance::ExecuteAnimationNode(AnimationNode* node)
min = Fix0;
max = ftofix(node->GetAnimation()->Length);
break;
case AnimationNode::CustomNode:
// No execution necessary
return true;
case AnimationNode::LinearInterpolationNode:
provider = node->GetWeightProvider();
min = Fix0;
@ -1340,6 +1467,8 @@ bool StdMeshInstance::ExecuteAnimationNode(AnimationNode* node)
assert(false);
break;
}
assert(provider);
const C4Real old_value = provider->Value;
if (!provider->Execute())

View File

@ -361,6 +361,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() {}
virtual void ClearPointers(class C4Object* pObj) {}
};
// A node in the animation tree
@ -370,10 +371,11 @@ public:
friend class StdMeshInstance;
friend class StdMeshUpdate;
public:
enum NodeType { LeafNode, LinearInterpolationNode };
enum NodeType { LeafNode, CustomNode, LinearInterpolationNode };
AnimationNode();
AnimationNode(const StdMeshAnimation* animation, ValueProvider* position);
AnimationNode(const StdMeshBone* bone, const StdMeshTransformation& trans);
AnimationNode(AnimationNode* child_left, AnimationNode* child_right, ValueProvider* weight);
~AnimationNode();
@ -395,6 +397,7 @@ public:
void CompileFunc(StdCompiler* pComp, const StdMesh* Mesh);
void DenumeratePointers();
void ClearPointers(class C4Object* pObj);
protected:
int Slot;
@ -410,6 +413,12 @@ public:
ValueProvider* Position;
} Leaf;
struct
{
unsigned int BoneIndex;
StdMeshTransformation* Transformation;
} Custom;
struct
{
AnimationNode* ChildLeft;
@ -432,6 +441,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp, AttachedMesh* attach) = 0;
virtual void DenumeratePointers(AttachedMesh* attach) {}
virtual bool ClearPointers(class C4Object* pObj) { return true; }
};
typedef Denumerator*(*DenumeratorFactoryFunc)();
@ -458,6 +468,7 @@ public:
void CompileFunc(StdCompiler* pComp, DenumeratorFactoryFunc Factory);
void DenumeratePointers();
bool ClearPointers(class C4Object* pObj);
private:
unsigned int ParentBone;
@ -486,6 +497,7 @@ public:
AnimationNode* PlayAnimation(const StdStrBuf& animation_name, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight);
AnimationNode* PlayAnimation(const StdMeshAnimation& animation, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight);
AnimationNode* PlayAnimation(const StdMeshBone* bone, const StdMeshTransformation& trans, int slot, AnimationNode* sibling, ValueProvider* weight);
void StopAnimation(AnimationNode* node);
AnimationNode* GetAnimationNodeByNumber(unsigned int number);
@ -494,6 +506,7 @@ public:
// Set new value providers for a node's position or weight - cannot be in
// class AnimationNode since we need to mark BoneTransforms dirty.
void SetAnimationPosition(AnimationNode* node, ValueProvider* position);
void SetAnimationBoneTransform(AnimationNode* node, const StdMeshTransformation& trans);
void SetAnimationWeight(AnimationNode* node, ValueProvider* weight);
// Update animations; call once a frame
@ -541,6 +554,7 @@ public:
void CompileFunc(StdCompiler* pComp, AttachedMesh::DenumeratorFactoryFunc Factory);
void DenumeratePointers();
void ClearPointers(class C4Object* pObj);
const StdMesh& GetMesh() const { assert(Mesh != NULL); return *Mesh; }
@ -548,6 +562,7 @@ protected:
typedef std::vector<AnimationNode*> AnimationNodeList;
AnimationNodeList::iterator GetStackIterForSlot(int slot, bool create);
void InsertAnimationNode(AnimationNode* node, int slot, AnimationNode* sibling, ValueProvider* weight);
bool ExecuteAnimationNode(AnimationNode* node);
void ApplyBoneTransformToVertices(const std::vector<StdSubMesh::Vertex>& mesh_vertices, std::vector<StdMeshVertex>& instance_vertices);

View File

@ -348,6 +348,53 @@ float StdMeshMatrix::Determinant() const
- a[0][0]*a[1][2]*a[2][1] - a[0][1]*a[1][0]*a[2][2] - a[0][2]*a[1][1]*a[2][0];
}
StdMeshTransformation StdMeshMatrix::Decompose() const
{
// Extract the scale part of the matrix
const float sx = sqrt(a[0][0]*a[0][0] + a[1][0]*a[1][0] + a[2][0]*a[2][0]);
const float sy = sqrt(a[0][1]*a[0][1] + a[1][1]*a[1][1] + a[2][1]*a[2][1]);
const float sz = sqrt(a[0][2]*a[0][2] + a[1][2]*a[1][2] + a[2][2]*a[2][2]);
// What remains is the rotation part
// TODO: This can be optimized by not doing the full matrix multiplication
StdMeshMatrix rot = Scale(1.0f/sx, 1.0f/sy, 1.0f/sz) * *this;
// Note that this does not work for skew matrices -- we cannot
// represent skews in StdMeshTransformation
const float cos_angle = 0.5f * (rot.a[0][0] + rot.a[1][1] + rot.a[2][2] - 1.0f);
const float rdx = rot.a[2][1] - rot.a[1][2];
const float rdy = rot.a[0][2] - rot.a[2][0];
const float rdz = rot.a[1][0] - rot.a[0][1];
const float det = sqrt(rdx*rdx + rdy*rdy + rdz*rdz);
const float rx = (rot.a[2][1] - rot.a[1][2]) / det;
const float ry = (rot.a[0][2] - rot.a[2][0]) / det;
const float rz = (rot.a[1][0] - rot.a[0][1]) / det;
const float angle = acos(cos_angle);
StdMeshTransformation trans;
trans.scale.x = sx;
trans.scale.y = sy;
trans.scale.z = sz;
trans.rotate = StdMeshQuaternion::AngleAxis(acos(cos_angle), StdMeshVector::Translate(rx, ry, rz));
trans.translate.x = a[0][3];
trans.translate.y = a[1][3];
trans.translate.z = a[2][3];
#if 0
// Double check that the result is correct. This check will fail if
// the original matrix has skew components.
StdMeshMatrix mat2 = StdMeshMatrix::Transform(trans);
for(unsigned int i = 0; i < 3; ++i)
for(unsigned int j = 0; j < 4; ++j)
assert( fabs(mat2.a[i][j] - a[i][j]) < 1e-3);
#endif
return trans;
}
StdMeshMatrix operator*(const StdMeshMatrix& lhs, const StdMeshMatrix& rhs)
{
StdMeshMatrix m;

View File

@ -95,6 +95,7 @@ public:
float operator()(int i, int j) const { return a[i][j]; }
float Determinant() const;
StdMeshTransformation Decompose() const;
private:
// 3x3 orthogonal + translation in last column

View File

@ -82,13 +82,13 @@ void StdMeshMaterialUpdate::Add(const StdMeshMaterial* material)
}
StdMeshUpdate::StdMeshUpdate(const StdMesh& old_mesh):
OldMesh(&old_mesh), BoneNames(OldMesh->GetNumBones())
OldMesh(&old_mesh), BoneNamesByIndex(OldMesh->GetNumBones())
{
for(std::map<StdCopyStrBuf, StdMeshAnimation>::const_iterator iter = OldMesh->Animations.begin(); iter != OldMesh->Animations.end(); ++iter)
AnimationNames[&iter->second] = iter->first;
for(unsigned int i = 0; i < OldMesh->GetNumBones(); ++i)
BoneNames[i] = OldMesh->GetBone(i).Name;
BoneNamesByIndex[i] = OldMesh->GetBone(i).Name;
}
void StdMeshUpdate::Update(StdMeshInstance* instance, const StdMesh& new_mesh) const
@ -110,7 +110,7 @@ void StdMeshUpdate::Update(StdMeshInstance* instance, const StdMesh& new_mesh) c
// in the updated mesh, then detach the mesh from its parent
if(instance->AttachParent)
{
if(!instance->AttachParent->SetChildBone(BoneNames[instance->AttachParent->ChildBone]))
if(!instance->AttachParent->SetChildBone(BoneNamesByIndex[instance->AttachParent->ChildBone]))
{
bool OwnChild = instance->AttachParent->OwnChild;
instance->AttachParent->Parent->DetachMesh(instance->AttachParent->Number);
@ -127,7 +127,7 @@ void StdMeshUpdate::Update(StdMeshInstance* instance, const StdMesh& new_mesh) c
std::vector<unsigned int> Removal;
for(StdMeshInstance::AttachedMeshIter iter = instance->AttachedMeshesBegin(); iter != instance->AttachedMeshesEnd(); ++iter)
{
if(!(*iter)->SetParentBone(BoneNames[(*iter)->ParentBone]))
if(!(*iter)->SetParentBone(BoneNamesByIndex[(*iter)->ParentBone]))
{
// Do not detach the mesh right here so we can finish iterating over
// all attached meshes first.
@ -166,6 +166,15 @@ bool StdMeshUpdate::UpdateAnimationNode(StdMeshInstance* instance, const StdMesh
provider->Value = BoundBy(provider->Value, min, max);
return true;
}
case StdMeshInstance::AnimationNode::CustomNode:
{
// Update bone index by bone name
StdCopyStrBuf bone_name = BoneNamesByIndex[node->Custom.BoneIndex];
const StdMeshBone* bone = new_mesh.GetBoneByName(bone_name);
if(!bone) return false;
node->Custom.BoneIndex = bone->Index;
return true;
}
case StdMeshInstance::AnimationNode::LinearInterpolationNode:
{
const bool left_result = UpdateAnimationNode(instance, new_mesh, node->GetLeftChild());

View File

@ -64,7 +64,7 @@ private:
const StdMesh* OldMesh;
std::map<const StdMeshAnimation*, StdCopyStrBuf> AnimationNames;
std::vector<StdCopyStrBuf> BoneNames;
std::vector<StdCopyStrBuf> BoneNamesByIndex;
};
#endif

View File

@ -34,9 +34,7 @@
#include <C4GameVersion.h>
#include <C4Language.h>
#ifdef DEBUGREC
#include <C4Record.h>
#endif
C4DefList::C4DefList()
{
@ -396,13 +394,14 @@ void C4DefList::CallEveryDefinition()
{
for (Table::iterator it = table.begin(); it != table.end(); ++it)
{
#ifdef DEBUGREC
// TODO: Might not be synchronous on runtime join since is run by joining
// client but not by host. Might need to go to Synchronize().
char sz[32+1];
strncpy(sz, it->first.ToString(), 32+1);
AddDbgRec(RCT_Definition, sz, 32);
#endif
if (Config.General.DebugRec)
{
// TODO: Might not be synchronous on runtime join since is run by joining
// client but not by host. Might need to go to Synchronize().
char sz[32+1];
strncpy(sz, it->first.ToString(), 32+1);
AddDbgRec(RCT_Definition, sz, 32);
}
C4AulParSet Pars(C4VPropList(it->second));
it->second->Call(PSF_Definition, &Pars);
}

View File

@ -201,6 +201,9 @@ C4ValueProviderX::C4ValueProviderX(C4Object* object, C4Real pos, C4Real begin, C
bool C4ValueProviderX::Execute()
{
// Object might have been removed
if(!Object) return false;
Value += (End - Begin) * (Object->xdir) / Length;
if (End > Begin)
@ -242,6 +245,9 @@ C4ValueProviderY::C4ValueProviderY(C4Object* object, C4Real pos, C4Real begin, C
bool C4ValueProviderY::Execute()
{
// Object might have been removed
if(!Object) return false;
Value += (End - Begin) * (Object->ydir) / Length;
if (End > Begin)
@ -283,6 +289,9 @@ C4ValueProviderR::C4ValueProviderR(C4Object* object, C4Real begin, C4Real end):
bool C4ValueProviderR::Execute()
{
// Object might have been removed
if(!Object) return false;
C4Real r = Object->fix_r;
if(r < 0) r += 360;
@ -309,6 +318,9 @@ C4ValueProviderAbsX::C4ValueProviderAbsX(C4Object* object, C4Real pos, C4Real be
bool C4ValueProviderAbsX::Execute()
{
// Object might have been removed
if(!Object) return false;
Value += (End - Begin) * Abs(Object->xdir) / Length;
assert( (End >= Begin && Value >= Begin) || (End <= Begin && Value <= Begin));
@ -339,6 +351,9 @@ C4ValueProviderAbsY::C4ValueProviderAbsY(C4Object* object, C4Real pos, C4Real be
bool C4ValueProviderAbsY::Execute()
{
// Object might have been removed
if(!Object) return false;
Value += (End - Begin) * Abs(Object->ydir) / Length;
assert( (End >= Begin && Value >= Begin) || (End <= Begin && Value <= Begin));
@ -369,6 +384,9 @@ C4ValueProviderXDir::C4ValueProviderXDir(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderXDir::Execute()
{
// Object might have been removed
if(!Object) return false;
Value = Begin + (End - Begin) * Min<C4Real>(Abs(Object->xdir/MaxXDir), itofix(1));
return true;
}
@ -394,6 +412,9 @@ C4ValueProviderYDir::C4ValueProviderYDir(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderYDir::Execute()
{
// Object might have been removed
if(!Object) return false;
Value = Begin + (End - Begin) * Min<C4Real>(Abs(Object->ydir/MaxYDir), itofix(1));
return true;
}
@ -419,6 +440,9 @@ C4ValueProviderRDir::C4ValueProviderRDir(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderRDir::Execute()
{
// Object might have been removed
if(!Object) return false;
Value = Begin + (End - Begin) * Min<C4Real>(Abs(Object->rdir/MaxRDir), itofix(1));
return true;
}
@ -444,6 +468,9 @@ C4ValueProviderCosR::C4ValueProviderCosR(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderCosR::Execute()
{
// Object might have been removed
if(!Object) return false;
Value = Begin + (End - Begin) * Cos(Object->fix_r + Offset);
return true;
}
@ -469,6 +496,9 @@ C4ValueProviderSinR::C4ValueProviderSinR(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderSinR::Execute()
{
// Object might have been removed
if(!Object) return false;
Value = Begin + (End - Begin) * Sin(Object->fix_r + Offset);
return true;
}
@ -494,6 +524,9 @@ C4ValueProviderCosV::C4ValueProviderCosV(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderCosV::Execute()
{
// Object might have been removed
if(!Object) return false;
// TODO: Maybe we can optimize this by using cos(r) = x/sqrt(x*x+y*y), sin(r)=y/sqrt(x*x+y*y)
// plus addition theorems for sin or cos.
@ -523,6 +556,9 @@ C4ValueProviderSinV::C4ValueProviderSinV(C4Object* object, C4Real begin, C4Real
bool C4ValueProviderSinV::Execute()
{
// Object might have been removed
if(!Object) return false;
// TODO: Maybe we can optimize this by using cos(r) = x/sqrt(x*x+y*y), sin(r)=y/sqrt(x*x+y*y),
// plus addition theorems for sin or cos.
@ -551,6 +587,9 @@ C4ValueProviderAction::C4ValueProviderAction(C4Object* object):
bool C4ValueProviderAction::Execute()
{
// Object might have been removed
if(!Object) return false;
const C4Action& Action = Object->Action;
C4PropList* pActionDef = Object->GetAction();

View File

@ -87,6 +87,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -103,6 +104,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -119,6 +121,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -134,6 +137,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -150,6 +154,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -166,6 +171,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -182,6 +188,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -198,6 +205,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -214,6 +222,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -230,6 +239,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -246,6 +256,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -262,6 +273,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
C4Real Begin;
@ -278,6 +290,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp);
virtual void DenumeratePointers() { Object.DenumeratePointers(); }
virtual void ClearPointers(C4Object* pObj) { if(Object == pObj) Object = NULL; }
private:
C4ObjectPtr Object;
};

View File

@ -40,9 +40,7 @@
#include <C4Command.h>
#include <C4Viewport.h>
#include <C4MaterialList.h>
#ifdef DEBUGREC
#include <C4Record.h>
#endif
#include <C4SolidMask.h>
#include <C4Random.h>
#include <C4Log.h>
@ -127,6 +125,18 @@ void C4MeshDenumerator::DenumeratePointers(StdMeshInstance::AttachedMesh* attach
}
}
bool C4MeshDenumerator::ClearPointers(C4Object* pObj)
{
if(Object == pObj)
{
Object = NULL;
// Return false causes the attached mesh to be deleted by StdMeshInstance
return false;
}
return true;
}
static void DrawVertex(C4Facet &cgo, float tx, float ty, int32_t col, int32_t contact)
{
if (Inside<int32_t>(tx,cgo.X,cgo.X+cgo.Wdt) && Inside<int32_t>(ty,cgo.Y,cgo.Y+cgo.Hgt))
@ -322,14 +332,15 @@ void C4Object::AssignRemoval(bool fExitContents)
{
// check status
if (!Status) return;
#ifdef DEBUGREC
C4RCCreateObj rc;
memset(&rc, '\0', sizeof(rc));
rc.oei=Number;
if (Def && Def->GetName()) strncpy(rc.id, Def->GetName(), 32+1);
rc.x=GetX(); rc.y=GetY(); rc.ownr=Owner;
AddDbgRec(RCT_DsObj, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
C4RCCreateObj rc;
memset(&rc, '\0', sizeof(rc));
rc.oei=Number;
if (Def && Def->GetName()) strncpy(rc.id, Def->GetName(), 32+1);
rc.x=GetX(); rc.y=GetY(); rc.ownr=Owner;
AddDbgRec(RCT_DsObj, &rc, sizeof(rc));
}
// Destruction call in container
if (Contained)
{
@ -479,10 +490,19 @@ void C4Object::UpdateGraphics(bool fGraphicsChanged, bool fTemp)
}
// Keep mesh instance if it uses the same underlying mesh
if(!pMeshInstance || !pGraphics->Type == C4DefGraphics::TYPE_Mesh ||
if(!pMeshInstance || pGraphics->Type != C4DefGraphics::TYPE_Mesh ||
&pMeshInstance->GetMesh() != pGraphics->Mesh)
{
// If this mesh is attached somewhere, detach it before deletion
if(pMeshInstance && pMeshInstance->GetAttachParent() != NULL)
{
// TODO: If the new mesh has a bone with the same name, we could try updating...
StdMeshInstance::AttachedMesh* attach_parent = pMeshInstance->GetAttachParent();
attach_parent->Parent->DetachMesh(attach_parent->Number);
}
delete pMeshInstance;
if (pGraphics->Type == C4DefGraphics::TYPE_Mesh)
{
pMeshInstance = new StdMeshInstance(*pGraphics->Mesh, Def->GrowthType ? 1.0f : static_cast<float>(Con)/static_cast<float>(FullCon));
@ -840,8 +860,11 @@ void C4Object::SetOCF()
if ((Def->GrabPutGet & C4D_Grab_Put) || (Def->GrabPutGet & C4D_Grab_Get) || (OCF & OCF_Entrance))
OCF|=OCF_Container;
#ifdef DEBUGREC_OCF
C4RCOCF rc = { dwOCFOld, OCF, false };
AddDbgRec(RCT_OCF, &rc, sizeof(rc));
if (Config.General.DebugRec)
{
C4RCOCF rc = { dwOCFOld, OCF, false };
AddDbgRec(RCT_OCF, &rc, sizeof(rc));
}
#endif
}
@ -908,8 +931,11 @@ void C4Object::UpdateOCF()
if ((Def->GrabPutGet & C4D_Grab_Put) || (Def->GrabPutGet & C4D_Grab_Get) || (OCF & OCF_Entrance))
OCF|=OCF_Container;
#ifdef DEBUGREC_OCF
C4RCOCF rc = { dwOCFOld, OCF, true };
AddDbgRec(RCT_OCF, &rc, sizeof(rc));
if (Config.General.DebugRec)
{
C4RCOCF rc = { dwOCFOld, OCF, true };
AddDbgRec(RCT_OCF, &rc, sizeof(rc));
}
#endif
#ifdef _DEBUG
DEBUGREC_OFF
@ -1000,15 +1026,16 @@ bool C4Object::ExecLife()
void C4Object::Execute()
{
#ifdef DEBUGREC
// record debug
C4RCExecObj rc;
rc.Number=Number;
rc.fx=fix_x;
rc.fy=fix_y;
rc.fr=fix_r;
AddDbgRec(RCT_ExecObj, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
// record debug
C4RCExecObj rc;
rc.Number=Number;
rc.fx=fix_x;
rc.fy=fix_y;
rc.fr=fix_r;
AddDbgRec(RCT_ExecObj, &rc, sizeof(rc));
}
// reset temporary marker
Marker = 0;
// OCF
@ -1815,10 +1842,8 @@ bool C4Object::Promote(int32_t torank, bool exception, bool fForceRankName)
void C4Object::ClearPointers(C4Object *pObj)
{
// TODO: Clear pointers on mesh instance:
// Check for attach children using pObj's mesh instance
// Check for animation nodes refering to pObj (Anim_X, ...).
// mesh attachments and animation nodes
if(pMeshInstance) pMeshInstance->ClearPointers(pObj);
// effects
if (pEffects) pEffects->ClearPointers(pObj);
// contents/contained: not necessary, because it's done in AssignRemoval and StatusDeactivate

View File

@ -90,6 +90,7 @@ public:
virtual void CompileFunc(StdCompiler* pComp, StdMeshInstance::AttachedMesh* attach);
virtual void DenumeratePointers(StdMeshInstance::AttachedMesh* attach);
virtual bool ClearPointers(C4Object* pObj);
};
class C4Action

View File

@ -1830,6 +1830,56 @@ static Nillable<int> FnPlayAnimation(C4Object *Obj, C4String *szAnimation, int i
return n_node->GetNumber();
}
static Nillable<int> FnTransformBone(C4Object *Obj, C4String *szBoneName, C4ValueArray* Transformation, int iSlot, C4ValueArray* WeightProvider, Nillable<int> iSibling, Nillable<int> iAttachNumber)
{
if (!Obj) return C4Void();
if (!Obj->pMeshInstance) return C4Void();
if (iSlot == 0) return C4Void(); // Reserved for ActMap animations
if (!Transformation) return C4Void();
if (!WeightProvider) return C4Void();
StdMeshInstance* Instance = Obj->pMeshInstance;
if (!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if (!Attached || !Attached->OwnChild) return C4Void();
Instance = Attached->Child;
}
StdMeshInstance::AnimationNode* s_node = NULL;
if (!iSibling.IsNil())
{
s_node = Instance->GetAnimationNodeByNumber(iSibling);
if (!s_node || s_node->GetSlot() != iSlot) return C4Void();
}
const StdMeshBone* bone = Instance->GetMesh().GetBoneByName(szBoneName->GetData());
if(!bone) return C4Void();
StdMeshInstance::ValueProvider* w_provider = CreateValueProviderFromArray(Obj, *WeightProvider);
if (!w_provider) return C4Void();
StdMeshMatrix matrix;
if (!C4ValueToMatrix(*Transformation, &matrix))
throw new C4AulExecError("TransformBone: Transformation is not a valid 3x4 matrix");
// For bone transformations we cannot use general matrix transformations, but we use decomposed
// translate, scale and rotation components (represented by the StdMeshTransformation class). This
// is less generic since it does not support skewing.
// Still, in the script API we want to expose a matrix parameter so that the convenient Trans_*
// functions can be used. We decompose the passed matrix at this point. If the matrix indeed has
// skewing components, the results will probably look strange since the decomposition would yield
// bogus values, however I don't think that's a practical use case. In the worst case we could add
// a check here and return nil if the matrix cannot be decomposed.
StdMeshTransformation trans = matrix.Decompose();
StdMeshInstance::AnimationNode* n_node = Instance->PlayAnimation(bone, trans, iSlot, s_node, w_provider);
if (!n_node) return C4Void();
return n_node->GetNumber();
}
static bool FnStopAnimation(C4Object *Obj, Nillable<int> iAnimationNumber, Nillable<int> iAttachNumber)
{
if (!Obj) return false;
@ -1974,6 +2024,35 @@ static bool FnSetAnimationPosition(C4Object *Obj, Nillable<int> iAnimationNumber
return true;
}
static bool FnSetAnimationBoneTransform(C4Object *Obj, Nillable<int> iAnimationNumber, C4ValueArray* Transformation, Nillable<int> iAttachNumber)
{
if (!Obj) return false;
if (!Obj->pMeshInstance) return false;
if (iAnimationNumber.IsNil()) return false; // distinguish nil from 0
StdMeshInstance* Instance = Obj->pMeshInstance;
if (!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if (!Attached || !Attached->OwnChild) return false;
Instance = Attached->Child;
}
StdMeshInstance::AnimationNode* node = Instance->GetAnimationNodeByNumber(iAnimationNumber);
// slot 0 is reserved for ActMap animations
if (!node || node->GetSlot() == 0 || node->GetType() != StdMeshInstance::AnimationNode::CustomNode) return false;
StdMeshMatrix matrix;
if (!C4ValueToMatrix(*Transformation, &matrix))
throw new C4AulExecError("TransformBone: Transformation is not a valid 3x4 matrix");
// Here the same remark applies as in FnTransformBone
StdMeshTransformation trans = matrix.Decompose();
Instance->SetAnimationBoneTransform(node, trans);
return true;
}
static bool FnSetAnimationWeight(C4Object *Obj, Nillable<int> iAnimationNumber, C4ValueArray* WeightProvider, Nillable<int> iAttachNumber)
{
if (!Obj) return false;
@ -2513,6 +2592,7 @@ void InitObjectFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "ExecuteCommand", FnExecuteCommand);
AddFunc(pEngine, "PlayAnimation", FnPlayAnimation);
AddFunc(pEngine, "TransformBone", FnTransformBone);
AddFunc(pEngine, "StopAnimation", FnStopAnimation);
AddFunc(pEngine, "GetRootAnimation", FnGetRootAnimation);
AddFunc(pEngine, "GetAnimationLength", FnGetAnimationLength);
@ -2520,6 +2600,7 @@ void InitObjectFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "GetAnimationPosition", FnGetAnimationPosition);
AddFunc(pEngine, "GetAnimationWeight", FnGetAnimationWeight);
AddFunc(pEngine, "SetAnimationPosition", FnSetAnimationPosition);
AddFunc(pEngine, "SetAnimationBoneTransform", FnSetAnimationBoneTransform);
AddFunc(pEngine, "SetAnimationWeight", FnSetAnimationWeight);
AddFunc(pEngine, "AttachMesh", FnAttachMesh);
AddFunc(pEngine, "DetachMesh", FnDetachMesh);

View File

@ -109,9 +109,8 @@ void C4LSectors::Add(C4Object *pObj, C4ObjectList *pMainList)
{
pSct->ObjectShapes.Add(pObj, C4ObjectList::stMain, pMainList);
}
#ifdef DEBUGREC
pObj->Area.DebugRec(pObj, 'A');
#endif
if (Config.General.DebugRec)
pObj->Area.DebugRec(pObj, 'A');
}
void C4LSectors::Update(C4Object *pObj, C4ObjectList *pMainList)
@ -152,9 +151,8 @@ void C4LSectors::Update(C4Object *pObj, C4ObjectList *pMainList)
}
// Update area
pObj->Area = NewArea;
#ifdef DEBUGREC
pObj->Area.DebugRec(pObj, 'U');
#endif
if (Config.General.DebugRec)
pObj->Area.DebugRec(pObj, 'U');
}
void C4LSectors::Remove(C4Object *pObj)
@ -187,9 +185,8 @@ void C4LSectors::Remove(C4Object *pObj)
// Remove from all sectors in shape area
for (pSct = pObj->Area.First(); pSct; pSct = pObj->Area.Next(pSct))
pSct->ObjectShapes.Remove(pObj);
#ifdef DEBUGREC
pObj->Area.DebugRec(pObj, 'R');
#endif
if (Config.General.DebugRec)
pObj->Area.DebugRec(pObj, 'R');
}
void C4LSectors::AssertObjectNotInList(C4Object *pObj)
@ -338,7 +335,6 @@ C4ObjectList *C4LArea::NextObjectShapes(C4ObjectList *pPrev, C4LSector **ppSct)
return &(*ppSct)->ObjectShapes;
}
#ifdef DEBUGREC
void C4LArea::DebugRec(class C4Object *pObj, char cMarker)
{
C4RCArea rc;
@ -352,4 +348,3 @@ void C4LArea::DebugRec(class C4Object *pObj, char cMarker)
rc.out = !!pOut;
AddDbgRec(RCT_Area, &rc, sizeof(C4RCArea));
}
#endif

View File

@ -126,9 +126,7 @@ public:
{ *ppSct=NULL; return NextObjectShapes(NULL, ppSct); }
C4ObjectList *NextObjectShapes(C4ObjectList *pPrev, C4LSector **ppSct); // get next object shapes list of this area
#ifdef DEBUGREC
void DebugRec(class C4Object *pObj, char cMarker);
#endif
};
#endif

View File

@ -57,14 +57,15 @@ void C4Shape::Clear()
void C4Shape::Rotate(C4Real Angle, bool bUpdateVertices)
{
#ifdef DEBUGREC
C4RCRotVtx rc;
rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt; rc.r=Angle;
int32_t i = 0;
for (; i<4; ++i)
{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
AddDbgRec(RCT_RotVtx1, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt; rc.r=Angle;
for (; i<4; ++i)
{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
AddDbgRec(RCT_RotVtx1, &rc, sizeof(rc));
}
int32_t cnt,nvtx,nvty,nwdt,nhgt;
C4Real mtx[4];
@ -123,12 +124,13 @@ void C4Shape::Rotate(C4Real Angle, bool bUpdateVertices)
}
Wdt = nwdt;
Hgt = nhgt;
#ifdef DEBUGREC
rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt;
for (i=0; i<4; ++i)
{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
AddDbgRec(RCT_RotVtx2, &rc, sizeof(rc));
#endif
if (Config.General.DebugRec)
{
rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt;
for (i=0; i<4; ++i)
{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
AddDbgRec(RCT_RotVtx2, &rc, sizeof(rc));
}
}
void C4Shape::Stretch(int32_t iCon, bool bUpdateVertices)

View File

@ -258,6 +258,7 @@ LRESULT APIENTRY ViewportWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPa
// compute scancode
C4KeyCode scancode = (((unsigned int)lParam) >> 16) & 0xFF;
ConvertToUnixScancode(wParam, &scancode);
// Process message
switch (uMsg)
@ -472,6 +473,7 @@ LRESULT APIENTRY DialogWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPara
// compute scancode
C4KeyCode scancode = (((unsigned int)lParam) >> 16) & 0xFF;
ConvertToUnixScancode(wParam, &scancode);
// Process message
switch (uMsg)

View File

@ -887,33 +887,36 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
throw new C4AulExecError("using removed object");
#ifdef DEBUGREC_SCRIPT
StdStrBuf sCallText;
if (pContext && pContext->GetObject())
sCallText.AppendFormat("Object(%d): ", pContext->GetObject()->Number);
sCallText.Append(pFunc->GetName());
sCallText.AppendChar('(');
for (int i=0; i<C4AUL_MAX_Par; ++i)
if (Config.General.DebugRec)
{
if (i) sCallText.AppendChar(',');
C4Value &rV = pPars[i];
if (rV.GetType() == C4V_String)
StdStrBuf sCallText;
if (pContext && pContext->GetObject())
sCallText.AppendFormat("Object(%d): ", pContext->GetObject()->Number);
sCallText.Append(pFunc->GetName());
sCallText.AppendChar('(');
for (int i=0; i<C4AUL_MAX_Par; ++i)
{
C4String *s = rV.getStr();
if (!s)
sCallText.Append("(Snull)");
else
if (i) sCallText.AppendChar(',');
C4Value &rV = pPars[i];
if (rV.GetType() == C4V_String)
{
sCallText.Append("\"");
sCallText.Append(s->GetData());
sCallText.Append("\"");
C4String *s = rV.getStr();
if (!s)
sCallText.Append("(Snull)");
else
{
sCallText.Append("\"");
sCallText.Append(s->GetData());
sCallText.Append("\"");
}
}
else
sCallText.Append(rV.GetDataString());
}
else
sCallText.Append(rV.GetDataString());
sCallText.AppendChar(')');
sCallText.AppendChar(';');
AddDbgRec(RCT_AulFunc, sCallText.getData(), sCallText.getLength()+1);
}
sCallText.AppendChar(')');
sCallText.AppendChar(';');
AddDbgRec(RCT_AulFunc, sCallText.getData(), sCallText.getLength()+1);
#endif
// Execute
@ -1076,9 +1079,12 @@ C4Value C4AulScriptFunc::Exec(C4PropList * p, C4Value pPars[], bool fPassErrors)
C4Value C4AulScript::DirectExec(C4Object *pObj, const char *szScript, const char *szContext, bool fPassErrors, C4AulScriptContext* context)
{
#ifdef DEBUGREC_SCRIPT
AddDbgRec(RCT_DirectExec, szScript, strlen(szScript)+1);
int32_t iObjNumber = pObj ? pObj->Number : -1;
AddDbgRec(RCT_DirectExec, &iObjNumber, sizeof(int32_t));
if (Config.General.DebugRec)
{
AddDbgRec(RCT_DirectExec, szScript, strlen(szScript)+1);
int32_t iObjNumber = pObj ? pObj->Number : -1;
AddDbgRec(RCT_DirectExec, &iObjNumber, sizeof(int32_t));
}
#endif
// profiler
AulExec.StartDirectExec();

View File

@ -26,9 +26,7 @@
#include <C4GameObjects.h>
#include <C4Game.h>
#include <C4Object.h>
#ifdef DEBUGREC
#include <C4Record.h>
#endif
void C4PropList::AddRef(C4Value *pRef)
{
@ -609,9 +607,12 @@ void C4PropList::SetPropertyByS(C4String * k, const C4Value & to)
//C4Property p(k, to);
//Properties.Add(p);
#ifdef DEBUGREC_SCRIPT
// deactivate this debugrec for now, because property orders seem to be out of sync
// after loading at the moment. might need to invastigate the cause later...
//if (k->GetCStr()) AddDbgRec(RCT_SetProperty, k->GetCStr(), strlen(k->GetCStr())+1);
if (Config.General.DebugRec)
{
// deactivate this debugrec for now, because property orders seem to be out of sync
// after loading at the moment. might need to invastigate the cause later...
//if (k->GetCStr()) AddDbgRec(RCT_SetProperty, k->GetCStr(), strlen(k->GetCStr())+1);
}
#endif
Properties.Add(C4Property(k, to));
}