Moving controls to script: Clonk movement

stable-5.2
Sven Eberhardt 2009-06-15 20:38:39 -04:00
parent 3dcf0de818
commit 943b47ebd5
12 changed files with 432 additions and 84 deletions

View File

@ -80,7 +80,7 @@
</row>
<row>
<col>Script (Standardwert)</col>
<col>Ausfuehrung des Scriptbefehls <em>PlayerControl</em> bzw. <em>PlayerControlRelease</em>. Siehe <emlink href="playercontrols.xml#Script">Script-Callbacks</emlink>.</col>
<col>Ausfuehrung des Scriptbefehls <em>PlayerControl</em>. Siehe <emlink href="playercontrols.xml#Script">Script-Callbacks</emlink>.</col>
</row>
<row>
<col>Menu</col>
@ -180,7 +180,7 @@
</part>
<h id="Script">Script-Callbacks</h>
<text>Die meisten Kommandos (abgesehen von asyrnchronen Kommandos im Spielermenue), rufen eine globale Scriptfunktion auf:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated)</code>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)</code>
<text>Fuer eine Erlaeuterung der Parameter siehe <funclink>PlayerControl</funclink>. Die Funktion erhaelt unter anderem den aufrufenden Spieler in iPlr, sowie das ausgefuehrte Kommando in iControl.</text>
<text>Fuer ein einfaches Beispiel sei in der globalen <em>PlayerControls.txt</em> folgendes Kommando definiert:</text>
<code>[ControlDefs]
@ -200,11 +200,11 @@
Control=Jump
Priority=50</code>
<text>Dies definiert eine Sprungtaste und die zugehoerige Standardbelegung auf der Tastatur fuer den ersten Spieler. Dazu folgendes Script zur Behandlung:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated)
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
{
// Welches Kommando wurde ausgeloest?
// Die Konstante CON_Jump wurde automatisch durch die Definition in PlayerControls.txt angelegt
if (control == CON_Jump)
if (control == CON_Jump &amp;&amp; !release)
{
// Sprungtaste gedrueckt. Der vom Spieler ausgewaehlte Clonk soll springen
var player_clonk = GetCursor(player);
@ -227,17 +227,17 @@
GUIDesc=Going underground
ExtraData=SHVL</code>
<text>Dabei sei SHVL die ID eines Schaufelobjektes. Im globalen Script kann zum Beispiel folgende, allgemeine Behandlung fuer unbekannte Kommandos stehen:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated)
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
{
// Behandlung bekannter Befehle
// [...]
// Befehl mit eigener Behandlung
if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat);
if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat, release);
// Unbekanntes Kommando
return false;
}</code>
<text>Und im Script der Schaufel:</text>
<code>func PlayerControl(int player, int control, int x, int y, int strength, bool repeated)
<code>func PlayerControl(int player, int control, int x, int y, int strength, bool repeated, bool release)
{
// Behandlung bekannter Befehle
// Grabkommando direkt an die Schaufel
@ -257,7 +257,7 @@
<h id="Hold">Gehaltene Tasten</h>
<text>Wird fuer ein Kommando das <em>Hold</em>-Flag gesetzt, so speichert die Engine den gegenwaertigen Tastenzustand fuer diese Taste. Solche Tasten haben einige Besonderheiten:</text>
<text><ul>
<li>Sie generieren beim Loslassen <funclink>PlayerControlRelease</funclink>-Aufrufe im Script.</li>
<li>Sie generieren auch beim Loslassen <funclink>PlayerControl</funclink>-Aufrufe mit gesetztem <em>Release</em>-Flag im Script.</li>
<li>Belegungen koennen mit den <em>Hold</em>/<em>Release</em>-Flags dauerhafte Tastendruecke emulieren.</li>
<li><emlink href="playercontrols.xml#Repeat">Tastenwiederholungen</emlink> werden erzeugt.</li>
<li>Der Haltezustand der Taste kann mit <funclink>GetPlayerControlState</funclink> im Script abgefragt werden.</li>
@ -269,13 +269,7 @@
GUIDesc=Walk left
Hold=1</code>
<text>Im Script wird die Richtung dann auf den Clonk uebertragen:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated)
{
if (control == CON_Left) return UpdateControlDir(player);
// ...
}
global func PlayerControlRelease(int player, int control, C4ID control_extra, int x, int y)
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
{
if (control == CON_Left) return UpdateControlDir(player);
// ...

View File

@ -185,7 +185,7 @@ class C4Player: public C4PlayerInfoCore
BOOL Sell2Home(C4Object *tobj);
BOOL DoWealth(int32_t change);
BOOL SetHostility(int32_t iOpponent, int32_t iHostility, BOOL fSilent=FALSE);
void CompileFunc(StdCompiler *pComp);
void CompileFunc(StdCompiler *pComp, bool fExact);
BOOL LoadRuntimeData(C4Group &hGroup);
bool ActivateMenuMain();
bool ActivateMenuTeamSelection(bool fFromMain);

View File

@ -201,6 +201,7 @@ class C4PlayerControlAssignmentSet
void MergeFrom(const C4PlayerControlAssignmentSet &Src, bool fLowPrio); // take over all assignments defined in Src
const char *GetName() const { return sName.getData(); }
bool IsWildcardName() const { return IsWildcardString(sName.getData()); }
C4PlayerControlAssignment *GetAssignmentByControlName(const char *szControlName);
void GetAssignmentsByKey(const C4PlayerControlDefs &rDefs, const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentPVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // match only by TriggerKey (last key of Combo) if fHoldKeysOnly
@ -264,6 +265,7 @@ class C4PlayerControl
C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations
C4PlayerControlRecentKeyList DownKeys; // keys currently held down
public:
// sync values
struct CSync
{
@ -294,7 +296,10 @@ class C4PlayerControl
void Clear();
void CompileFunc(StdCompiler *pComp);
bool operator ==(const CSync &cmp) const;
} Sync;
};
private:
CSync Sync;
// callbacks from Game.KeyboardInput
bool ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4KeyEventData &rKeyExtraData);
@ -319,6 +324,8 @@ class C4PlayerControl
void RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset); // register all keys into Game.KeyboardInput creating KeyBindings
bool IsGlobal() const { return iPlr==-1; }
const CSync::ControlDownState *GetControlDownState(int32_t iControl) const
{ return Sync.GetControlDownState(iControl); }
// callback from control queue
void ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl);

View File

@ -150,8 +150,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine); // add functions to engine
#define PSF_OnHostilityChange "~OnHostilityChange" // int iPlr1, int iPlr2, bool fNewHostility, bool fOldHostility
#define PSF_OnTeamSwitch "~OnTeamSwitch" // int iPlr1, int idNewTeam, int idOldTeam
#define PSF_OnOwnerRemoved "~OnOwnerRemoved"
#define PSF_PlayerControl "PlayerControl" // int iPlr, int iControl, C4ID idControlExtraData, int x, int y, int iStrength, bool fRepeated
#define PSF_PlayerControlRelease "PlayerControlRelease" // int iPlr, int iControl, C4ID idControlExtraData, int x, int y
#define PSF_PlayerControl "PlayerControl" // int iPlr, int iControl, C4ID idControlExtraData, int x, int y, int iStrength, bool fRepeated, bool fReleased
// Fx%s is automatically prefixed
#define PSFS_FxAdd "Add" // C4Object *pTarget, int iEffectNumber, C4String *szNewEffect, int iNewTimer, C4Value vNewEffectVar1, C4Value vNewEffectVar2, C4Value vNewEffectVar3, C4Value vNewEffectVar4

View File

@ -1824,7 +1824,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp)
{
if (!comp.fScenarioSection && comp.fExact)
{
pComp->Name("Game");
pComp->Name("Game");
pComp->Value(mkNamingAdapt(Time, "Time", 0));
pComp->Value(mkNamingAdapt(FrameCounter, "Frame", 0));
// pComp->Value(mkNamingAdapt(Control.ControlRate, "ControlRate", 0));
@ -1856,24 +1856,30 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp)
if (comp.fExact)
{
pComp->Value(mkNamingAdapt(Weather, "Weather"));
pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
pComp->Value(mkNamingAdapt(Landscape.Sky, "Sky"));
pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
pComp->Value(mkNamingAdapt(Landscape.Sky, "Sky"));
}
pComp->Value(mkNamingAdapt(mkNamingPtrAdapt(pGlobalEffects, "GlobalEffects"), "Effects"));
// scoreboard compiles into main level [Scoreboard]
if (!comp.fScenarioSection && comp.fExact)
pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard"));
if (!comp.fScenarioSection && comp.fExact)
{
// scoreboard compiles into main level [Scoreboard]
pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard"));
// Keyboard status of global keys synchronized for exact (runtime join) only; not for savegames,
// as keys might be released between a savegame save and its resume
//pComp->Value(GlobalPlayerControl);
}
if (comp.fPlayers)
{
assert(pComp->isDecompiler());
assert(pComp->isDecompiler());
// player parsing: Parse all players
// This doesn't create any players, but just parses existing by their ID
// Primary player ininitialization (also setting ID) is done by player info list
// Won't work this way for binary mode!
for (C4Player *pPlr=Players.First; pPlr; pPlr=pPlr->Next)
pComp->Value(mkNamingAdapt(*pPlr, FormatString("Player%d", pPlr->ID).getData()));
pComp->Value(mkNamingAdapt(mkParAdapt(*pPlr, comp.fExact), FormatString("Player%d", pPlr->ID).getData()));
}
}
@ -3231,7 +3237,8 @@ bool C4Game::InitPlayerControlSettings()
C4PlayerControlFile PlayerControlFile;
if (!PlayerControlFile.Load(Application.SystemGroup, C4CFN_PlayerControls, &MainSysLangStringTable)) { LogFatal("[!]Error loading player controls"); return false; }
PlayerControlDefs = PlayerControlFile.GetControlDefs();
PlayerControlAssignmentSets = PlayerControlFile.GetAssignmentSets();
PlayerControlAssignmentSets.Clear();
PlayerControlAssignmentSets.MergeFrom(PlayerControlFile.GetAssignmentSets(), false);
PlayerControlAssignmentSets.ResolveRefs(&PlayerControlDefs);
// And overwrites from config
//PlayerControlAssignmentSets.MergeFrom(Config.Controls.Assignments);

View File

@ -1411,7 +1411,7 @@ void C4Player::ObjectCommand2Obj(C4Object *cObj, int32_t iCommand, C4Object *pTa
else if (iMode & C4P_Command_Set) cObj->SetCommand(iCommand,pTarget,iX,iY,pTarget2,TRUE,iData);
}
void C4Player::CompileFunc(StdCompiler *pComp)
void C4Player::CompileFunc(StdCompiler *pComp, bool fExact)
{
assert(ID);
@ -1465,6 +1465,9 @@ void C4Player::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(Crew, "Crew" ));
pComp->Value(mkNamingAdapt(CrewInfoList.iNumCreated, "CrewCreated", 0));
pComp->Value(mkNamingPtrAdapt( pMsgBoardQuery, "MsgBoardQueries" ));
// Keys held down
pComp->Value(Control);
}
BOOL C4Player::LoadRuntimeData(C4Group &hGroup)
@ -1475,9 +1478,10 @@ BOOL C4Player::LoadRuntimeData(C4Group &hGroup)
// safety: Do nothing if playeer section is not even present (could kill initialized values)
if (!SSearch(pSource, FormatString("[Player%i]", ID).getData())) return FALSE;
// Compile (Search player section - runtime data is stored by unique player ID)
// Always compile exact. Exact data will not be present for savegame load, so it does not matter
assert(ID);
if(!CompileFromBuf_LogWarn<StdCompilerINIRead>(
mkNamingAdapt(*this, FormatString("Player%i", ID).getData()),
mkNamingAdapt(mkParAdapt(*this, true), FormatString("Player%i", ID).getData()),
StdStrBuf(pSource),
Game.GameText.GetFilePath()))
return FALSE;

View File

@ -292,7 +292,7 @@ bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp
void C4PlayerControlAssignmentSet::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("ControlSet")) { pComp->NameEnd(); pComp->excNotFound("ControlSet"); }
pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_Idtf), "Name", "None"));
pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_All), "Name", "None")); // can't do RCT_Idtf because of wildcards
pComp->Value(mkSTLContainerAdapt(Assignments, StdCompiler::SEP_NONE));
pComp->NameEnd();
}
@ -420,15 +420,31 @@ void C4PlayerControlAssignmentSets::MergeFrom(const C4PlayerControlAssignmentSet
{
const C4PlayerControlAssignmentSet &SrcSet = *i;
// overwrite if def of same name existed if it's not low priority anyway
C4PlayerControlAssignmentSet *pPrevSet = GetSetByName(SrcSet.GetName());
if (pPrevSet)
bool fIsWildcardSet = SrcSet.IsWildcardName();
if (!fIsWildcardSet)
{
pPrevSet->MergeFrom(SrcSet, fLowPrio);
C4PlayerControlAssignmentSet *pPrevSet = GetSetByName(SrcSet.GetName());
if (pPrevSet)
{
pPrevSet->MergeFrom(SrcSet, fLowPrio);
}
else
{
// new def: Append a copy
Sets.push_back(SrcSet);
}
}
else
{
// new def: Append a copy
Sets.push_back(SrcSet);
// source is a wildcard: Merge with all matching sets
for (AssignmentSetList::iterator j = Sets.begin(); j != Sets.end(); ++j)
{
C4PlayerControlAssignmentSet &DstSet = *j;
if (WildcardMatch(SrcSet.GetName(), DstSet.GetName()))
{
DstSet.MergeFrom(SrcSet, fLowPrio);
}
}
}
}
}
@ -597,7 +613,6 @@ bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4
// build a control packet and add control data instead. even for async controls later in chain, as they may be blocked by a sync handler
if (!pControlPacket) pControlPacket = new C4ControlPlayerControl(iPlr, fUp, rKeyExtraData);
pControlPacket->AddControl(iControlIndex, pAssignment->GetTriggerMode());
break;
}
}
}
@ -766,22 +781,11 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra
// player lost?
return false;
}
if (!fUp)
{
// control down
C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControl);
if (!pFunc) return false;
C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y), C4VInt(rKeyExtraData.iStrength), C4VBool(fRepeated));
return !!pFunc->Exec(NULL, &Pars);
}
else
{
// control up
C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControlRelease);
if (!pFunc) return false;
C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y));
return !!pFunc->Exec(NULL, &Pars);
}
// control down
C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControl);
if (!pFunc) return false;
C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y), C4VInt(rKeyExtraData.iStrength), C4VBool(fRepeated), C4VBool(fUp));
return !!pFunc->Exec(NULL, &Pars);
}

View File

@ -4368,7 +4368,7 @@ static C4Value FnGetPlayerVal(C4AulContext* cthr, C4Value* strEntry_C4V, C4Value
C4Player* pPlayer = Game.Players.Get(iPlr);
// get value
return GetValByStdCompiler(strEntry, strSection, iEntryNr, mkNamingAdapt(*pPlayer, "Player"));
return GetValByStdCompiler(strEntry, strSection, iEntryNr, mkNamingAdapt(mkParAdapt(*pPlayer, true), "Player"));
}
static C4Value FnGetPlayerInfoCoreVal(C4AulContext* cthr, C4Value* strEntry_C4V, C4Value* strSection_C4V, C4Value* iPlayer_C4V, C4Value *iEntryNr_C4V)
@ -6307,6 +6307,32 @@ static bool FnSetNextMission(C4AulContext *ctx, C4String *szNextMission, C4Strin
return true;
}
static long FnGetPlayerControlState(C4AulContext *ctx, long iPlr, long iControl)
{
// get control set to check
C4PlayerControl *pCheckCtrl = NULL;
if (iPlr == NO_OWNER)
{
//pCheckCtrl = Game.GlobalPlayerControls;
}
else
{
C4Player *pPlr = Game.Players.Get(iPlr);
if (pPlr)
{
pCheckCtrl = &(pPlr->Control);
}
}
// invalid player or no controls
if (!pCheckCtrl) return 0;
// query control
const C4PlayerControl::CSync::ControlDownState *pControlState = pCheckCtrl->GetControlDownState(iControl);
// no state means not down
if (!pControlState) return 0;
// otherwise take down-value
return pControlState->DownState.iStrength;
}
//=========================== C4Script Function Map ===================================
// defined function class
@ -6794,6 +6820,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "LocateFunc", FnLocateFunc);
AddFunc(pEngine, "PathFree", FnPathFree);
AddFunc(pEngine, "SetNextMission", FnSetNextMission);
AddFunc(pEngine, "GetPlayerControlState", FnGetPlayerControlState);
new C4AulDefCastFunc(pEngine, "ScoreboardCol", C4V_C4ID, C4V_Int);
new C4AulDefCastFunc(pEngine, "CastInt", C4V_Any, C4V_Int);
new C4AulDefCastFunc(pEngine, "CastBool", C4V_Any, C4V_Bool);

View File

@ -1,13 +1,200 @@
#strict 2
global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat)
// Functions to handle player controls (i.e., input keys)
// PlayerControlRelease
// Called by engine whenever a control is issued
// Forwards control to special handler or cursor
// Return whether handled
global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat, bool release)
{
Log("%d, %d, %i, %d, %d, %d, %v", plr, ctrl, spec_id, x,y,strength, repeat);
//Log("%d, %s, %i, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), spec_id, x,y,strength, repeat, release);
// Control handled by definition? Forward
if (spec_id) return spec_id->PlayerControl(plr, ctrl, x, y, strength, repeat, release);
// Forward control to cursor
var cursor = GetCursor(plr);
if (cursor) return cursor->ObjectControl(plr, ctrl, x,y, strength, repeat, release);
// No cursor? Nothing to handle control then
return false;
}
/* Object functions */
// To be called in an object context only!
// ObjectControl
// Called from PlayerControl when a control is issued to the cursor
// Return whether handled
global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
{
// Object controlled by plr
SetController(plr);
// Any control resets a previously given command
SetCommand(this, "None");
// Movement controls
if (ctrl==CON_Left || ctrl==CON_Right || ctrl==CON_Up || ctrl==CON_Down)
{
return ObjectControlMovement(plr, ctrl, strength, release);
}
// Unhandled control
var srelease = "";
if (release) srelease="Up";
Log("Unhandled: %d, %s%s, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), srelease, x,y,strength, repeat);
return false;
}
// ObjectControlMovement
// Called when CON_Left/Right/Up/Down controls are issued/released
// Return whether handled
global func ObjectControlMovement(int plr, int ctrl, int strength, bool release)
{
var proc = GetProcedure();
// Some specific movement controls
if (!release)
{
// Jump control
if (ctrl == CON_Up && proc == "WALK")
{
return PlayerObjectCommand(plr, false, "Jump");
}
if (proc == "SCALE") // Let go from scaling a wall
{
if (ctrl == CON_Left && GetDir() == DIR_Right) return ObjectComLetGo(-10);
if (ctrl == CON_Right && GetDir() == DIR_Left) return ObjectComLetGo(+10);
}
else if (proc == "HANGLE") // Let go from hangling the ceiling
{
if (ctrl == CON_Down) return ObjectComLetGo(0,0);
}
else if (proc == "FIGHT") // Clonk-to-Clonk-fight. Might want to implement some more sophisticated behaviour here?
{
// stop, but don't abort ComDir processing. May want to do Stop while holding a direction to run away?
if (ctrl == CON_Down) ObjectComStop();
}
// Make sure other selected Clonks are following
PlayerObjectCommand(plr, true, "Follow", this, GetX(), GetY());
// Direct turnaround if object is standing still. Valid for any procedure in OC
if (!GetXDir())
{
if (ctrl == CON_Left) SetDir(DIR_Left);
else if (ctrl == CON_Right) SetDir(DIR_Right);
}
}
return ObjectControlUpdateComdir(plr);
}
// ObjectControlUpdateComdir
// Updates ComDir of object based on current Con_*-directional controls
// Return whether actual, effective direction of movement changed
global func ObjectControlUpdateComdir(int plr)
{
// Generic movement: Update ComDir based on current control state
var new_comdir = GetPlayerConDir(plr, CON_Left, CON_Up, CON_Right, CON_Down);
var old_comdir = GetComDir();
if (new_comdir != old_comdir)
{
// ComDir changed. Update.
SetComDir(new_comdir);
//var s = "";
//if (GetPlayerControlState(plr, CON_Left)) s = Format("%sL", s);
//if (GetPlayerControlState(plr, CON_Up)) s = Format("%sU", s);
//if (GetPlayerControlState(plr, CON_Right)) s = Format("%sR", s);
//if (GetPlayerControlState(plr, CON_Down)) s = Format("%sD", s);
//s = Format("%s %s", s, ["Stop", "Up", "UpRight", "Right", "DownRight", "Down", "DownLeft", "Left", "UpLeft"][new_comdir]);
//Message("@%s", this, s);
// The control is only handled if it had an actual effect on the current movement direction of the Clonk
var old_cx,old_cy,new_cx,new_cy;
ComDir2XY(old_comdir, old_cx, old_cy);
ComDir2XY(new_comdir, new_cx, new_cy);
var is_handled;
var proc = GetProcedure();
if (proc == "WALK" || proc == "HANGLE" || proc == "PUSH" || proc == "PULL")
is_handled = (old_cx != new_cx) && !new_cy; // Only horizontal movement changed actual direction. Also, enfore clear Left/Right commands without leftover Up/Down
else if (proc == "SCALE")
is_handled = (old_cy != new_cy) && !new_cx; // Only vertical movement changed actual direction. Also, enfore clear Up/Down to prevent "Zuppel" in corner
else if (proc == "SWIM" || proc == "FLOAT" || proc == "DIG")
is_handled = (old_cx != new_cx || old_cy != new_cy); // Free 360 degree movement
else
is_handled = false;
return is_handled;
}
else
{
// ComDir did not change. -> The control was not handled
//Log("NoChange");
return false;
}
}
// GetPlayerControlName
// Helper function to turn CON_*-constants into strings
global func GetPlayerControlName(int ctrl)
{
if (ctrl == CON_Left ) return "Left";
if (ctrl == CON_Right ) return "Right";
if (ctrl == CON_Up ) return "Up";
if (ctrl == CON_Down ) return "Down";
if (ctrl == CON_Throw ) return "Throw";
if (ctrl == CON_Dig ) return "Dig";
return Format("Unknown(%d)", ctrl);
}
// GetPlayerConDir
// Return COMD_*-constant corresponding to current state of passed directional controls
global func GetPlayerConDir(int plr, int con_left, int con_up, int con_right, int con_down)
{
var x,y;
if (GetPlayerControlState(plr, con_left)) --x;
if (GetPlayerControlState(plr, con_up)) --y;
if (GetPlayerControlState(plr, con_right)) ++x;
if (GetPlayerControlState(plr, con_down)) ++y;
// Creating an array here for every keypress/release
// Would be so cool to have this static const. Guenther?
var dir_coms = [COMD_UpLeft, COMD_Up, COMD_UpRight, COMD_Left, COMD_None, COMD_Right, COMD_DownLeft, COMD_Down, COMD_DownRight];
return dir_coms[y*3+x+4];
}
// ComDir2XY
// Returns coordinate directions associated with a COMD_Constant
global func ComDir2XY(int comd, &x, &y)
{
// Creating an array here for every keypress/release
// Would be so cool to have this static const. Guenther?
x = [0,0,1,1,1,0,-1,-1,-1][comd];
y = [0,-1,-1,0,1,1,1,0,-1][comd];
return true;
}
global func PlayerControlRelease(int plr, int ctrl, id spec_id, int x, int y)
// PlayerObjectCommand
// Give a command to all selected Clonks of a player
global func PlayerObjectCommand(int plr, bool exclude_cursor, string command, object target, int tx, int ty, object target2)
{
Log("re %d, %d, %i, %d, %d", plr, ctrl, spec_id, x,y);
for (var i=exclude_cursor; i<GetSelectCount(plr); ++i)
{
var follow_clonk = GetCursor(plr, i);
if (follow_clonk)
{
SetCommand(follow_clonk,command,target,tx,ty);
}
}
return true;
}
}
// ObjectComStop
// Stop action and ComDir
global func ObjectComStop()
{
SetComDir();
SetAction("Idle");
if (!SetAction("Walk")) return false;
SetXDir(); SetYDir();
return true;
}
// ObjectComLetGo
// Let go from scaling or hangling
global func ObjectComLetGo(int vx, int vy)
{
if (!SetAction("Jump")) return false;
SetXDir(vx); SetYDir(vy);
return true;
}

View File

@ -3,10 +3,26 @@
[ControlDef]
Identifier=Left
GUIName=Left
GUIDesc=Walk left
GUIDesc=Move left
Hold=1
[ControlDef]
Identifier=Right
GUIName=Right
GUIDesc=Move right
Hold=1
[ControlDef]
Identifier=Up
GUIName=Up
GUIDesc=Move up or jump
Hold=1
[ControlDef]
Identifier=Down
GUIName=Down
GUIDesc=Move down
Hold=1
RepeatDelay=5
InitialRepeatDelay=35
[ControlDef]
Identifier=Throw
@ -16,23 +32,133 @@
RepeatDelay=5
InitialRepeatDelay=35
[ControlDef]
Identifier=Dig
GUIName=Dig
GUIDesc=Delve through the earth
[ControlDef]
Identifier=Test
GUIName=Test
GUIDesc=Test key
[ControlSets]
[ControlSet]
Name=Keyboard2
[Assignment]
Key=A
Control=Left
[Assignment]
Key=D
Control=Right
[Assignment]
Key=S
Control=Down
[Assignment]
Key=W
Control=Up
TriggerMode=Hold
[ControlSet]
Name=Keyboard2Classic
[Assignment]
Key=A
Control=Left
TriggerMode=Hold
[Assignment]
Key=A
Control=Right
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=A
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=A
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=D
Control=Right
TriggerMode=Hold
[Assignment]
Key=D
Control=Left
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=D
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=D
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=S
Control=Down
TriggerMode=Hold
[Assignment]
Key=S
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=S
Control=Left
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=S
Control=Right
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=W
Control=Up
TriggerMode=Hold
[Assignment]
Key=W
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=W
Control=Left
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=W
Control=Right
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[ControlSet]
Name=Keyboard2*
[Assignment]
Key=Q
Control=Throw
@ -42,19 +168,3 @@
Key=E
Control=Dig
Priority=50
[Assignment]
Key=A
Control=Left
TriggerMode=Hold
[Assignment]
Key=S
Control=Left
TriggerMode=Release | AlwaysUnhandled
[Assignment]
Key=Throw,Throw,Dig
ComboIsSequence=1
Control=Test
Priority=60

View File

@ -69,6 +69,7 @@ void TruncateBackslash(char *szFilename);
void MakeTempFilename(char *szFileName);
void MakeTempFilename(class StdStrBuf *sFileName);
bool WildcardListMatch(const char *szWildcardList, const char *szString); // match string in list like *.png|*.bmp
bool IsWildcardString(const char *szString); // does szString contain wildcard characters?
bool WildcardMatch(const char *szFName1, const char *szFName2);
bool TruncatePath(char *szPath);
// szBuffer has to be of at least _MAX_PATH length.

View File

@ -369,6 +369,14 @@ bool WildcardListMatch(const char *szWildcardList, const char *szString)
return false;
}
bool IsWildcardString(const char *szString)
{
// safety
if(!szString) return false;
// known wildcard characters: *?
return (SCharCount('?', szString)>0) || (SCharCount('*', szString)>0);
}
bool WildcardMatch(const char *szWildcard, const char *szString)
{
// safety