stable-5.2
Sven Eberhardt 2009-08-29 23:12:31 +02:00
commit 4407fb040a
55 changed files with 2488 additions and 1204 deletions

View File

@ -53,6 +53,8 @@ set(OC_CLONK_SOURCES
src/control/C4GameParameters.h
src/control/C4GameSave.cpp
src/control/C4GameSave.h
src/control/C4PlayerControl.cpp
src/control/C4PlayerControl.h
src/control/C4PlayerInfoConflicts.cpp
src/control/C4PlayerInfo.cpp
src/control/C4PlayerInfo.h
@ -442,21 +444,23 @@ add_executable(clonk WIN32 MACOSX_BUNDLE
${OC_SYSTEM_SOURCES}
)
source_group("Unsorted" .*)
source_group("Library\\Platform abstraction" src/platform/.*)
source_group("Library\\Utility" src/lib/.*)
source_group("Library\\C4Group" src/c4group/.*)
source_group("Library\\Graphics" src/lib/texture/.*)
source_group("GUI" src/gui/.*)
source_group("Network" src/network/.*)
source_group("Game\\Objects" src/game/object/.*)
source_group("Game\\Scenario" src/game/landscape/.*)
source_group("Game\\Player" src/game/player/.*)
source_group("Game\\Script" src/game/script/.*)
source_group("Scripting" src/script/.*)
source_group("Config" src/config/.*)
source_group("Control" src/control/.*)
source_group("Editing" src/editor/.*)
if(PROJECT_FOLDERS)
source_group("Unsorted" .*)
source_group("Library\\Platform abstraction" src/platform/.*)
source_group("Library\\Utility" src/lib/.*)
source_group("Library\\C4Group" src/c4group/.*)
source_group("Library\\Graphics" src/lib/texture/.*)
source_group("GUI" src/gui/.*)
source_group("Network" src/network/.*)
source_group("Game\\Objects" src/game/object/.*)
source_group("Game\\Scenario" src/game/landscape/.*)
source_group("Game\\Player" src/game/player/.*)
source_group("Game\\Script" src/game/script/.*)
source_group("Scripting" src/script/.*)
source_group("Config" src/config/.*)
source_group("Control" src/control/.*)
source_group("Editing" src/editor/.*)
endif()
add_executable(c4group
src/c4group/c4group_ng.cpp
@ -528,6 +532,7 @@ include(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(vasprintf HAVE_VASPRINTF)
# User selectable options
option(PROJECT_FOLDERS "Put source files into subfolders in project file" ON)
option(USE_GL "Enable OpenGL support" ON)
if(APPLE)
SET(INITIAL_USE_SDL_MAINLOOP_VALUE ON)

View File

@ -0,0 +1,327 @@
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<!DOCTYPE doc SYSTEM "../clonk.dtd">
<?xml-stylesheet type="text/xsl" href="../clonk.xsl"?>
<doc>
<title>Spielersteuerung</title>
<h id="Controls">Spielersteuerung</h>
<part>
<text>Ab OC erlaubt die Engine, Steuerungskommandos fuer Spieler komplett frei zu definieren. Eigene Tastaturkommandos koennen hinzugefuegt oder veraendert werden. Alle unterstuetzten Eingabegeraete wie Maus, Tastatur und Gamepads koennen frei belegt werden und Kommandos koennen aus beliebigen Tastenkombinationen oder Sequenzen bestehen.</text>
<h id="ControlFiles">PlayerControls.txt</h>
<text>Alle Steuerungsbefehle, die ein Spieler an das Spiel geben kann, werden in der Datei PlayerControls.txt definiert. Die Standardtasten sowie deren Standardbelegung fuer verschiedene Eingabegeraete befinden sich in der globalen Definitionsdatei in der System.c4g. Objektdefinitionen und Szenarien koennen weitere Tasten in ihrer lokalen System.c4g hinzufuegen oder die Parameter vorhandener Steuerkommandos ueberladen**.</text>
<text>Zusaetzliche Dateien PlayerControls.txt koennen in Sprachpaketen abgelegt werden, um die Standardbelegungen der Tasten bei verschiedenen, geladenen Sprachen an die Tastaturen des jeweiligen Landes anzupassen**.</text>
<part>
<h id="ControlDefs">Sektion [ControlDefs]</h>
<text>Definition der moeglichen Spielerkommandos. Nicht gueltig in Sprachpaketen. Dieser Sektion untergeordnet:
<text><table>
<caption id="ControlDef">Beliebig viele Sektionen [ControlDef]</caption>
<rowh>
<col>Wert</col>
<col>Datentyp</col>
<col>Beschreibung</col>
</rowh>
<row>
<col>Identifier</col>
<col>Zeichenfolge (Max. 96 Zeichen)</col>
<col>Intern benutzter Name zur Identifikation des Kommandos. Das Kommando wird unter diesem Namen in Standardbelegungen referenziert und im Script als CON_Name-Konstante vordefiniert. Der Name sollte folglich im Script gueltig sein, d.h. nur aus Buchstaben, Zahlen sowie _ bestehen. Insbesondere sollten keine Leerzeichen oder deutsche Umlaute verwendet werden. Zur Vermeidung von Konflikten gelten in szenarienlokalen sowie Objektpaketlokalen Definitionen dieselben Benennungsregeln wie zur Vergabe von Objekt-IDs.</col>
</row>
<row>
<col>GUIName</col>
<col>Zeichenfolge</col>
<col>Name, der dem Spieler im Steuerungskonfigurationsdialog sowie in den Steuerungstooltips angezeigt wird. Lokalisierte Zeichenketten koennen aus dem zugehoerigen StringTable refeenziert werden ($Name$).</col>
</row>
<row>
<col>GUIDesc</col>
<col>Zeichenfolge</col>
<col>Erlaeuternde Beschreibung, die dem Spieler im Steuerungskonfigurationsdialog angezeigt wird. Lokalisierte Zeichenketten koennen aus dem zugehoerigen StringTable refeenziert werden ($Name$).</col>
</row>
<row>
<col>Global</col>
<col>Boolean</col>
<col>Wenn wahr, ist dies eine globale, d.h. keinem Spieler zugeordnete Definition. Siehe <emlink href="playercontrols.xml#Globals">Globale Definitionen</emlink>.</col>
</row>
<row>
<col>Hold</col>
<col>Boolean</col>
<col>Wenn wahr, wird das Kommando als ein gehaltenes Kommando interpretiert. Ein solches Kommando speichert, ob die Steuerungstaste gedrueckt ist und generiert beim Loslassen ein zusaetzliches Scriptereignis. Siehe <emlink href="playercontrols.xml#Hold">Gehaltene Tasten</emlink>.</col>
</row>
<row>
<col>RepeatDelay</col>
<col>Integer</col>
<col>Nur gueltig wenn <em>Hold</em> wahr. Wenn groesser 0, generiert die Taste im gehaltenen Zustand im angegebenen Abstand (in Frames) weitere Scriptereignisse. Siehe <emlink href="playercontrols.xml#Repeat">Tastenwiederholungen</emlink>.</col>
</row>
<row>
<col>InitialRepeatDelay</col>
<col>Integer</col>
<col>Wenn angegeben, kann die Wartezeit fuer das erste Tastenwiederholungsereignis geaendert werden. Siehe <emlink href="playercontrols.xml#Repeat">Tastenwiederholungen</emlink>.</col>
</row>
<row>
<col>DefaultDisabled</col>
<col>Boolean</col>
<col>Wenn wahr, ist das Kommando im Normalfall deaktiviert und muss erst per Script aktiviert werden. Nuetzlich fuer Kommandos, die nur in sehr speziellen Situationen benoetigt werden. Siehe <emlink href="playercontrols.xml#Deactivate">Deaktivierte Kommandos</emlink>.</col>
</row>
<row>
<col>ExtraData</col>
<col>C4ID</col>
<col>Optionale ID, die an die Scriptfunktion uebergeben wird. Siehe <emlink href="playercontrols.xml#ExtraData">ExtraData</emlink>.</col>
</row>
<row>
<col>Action</col>
<col>Zeichenfolge</col>
<col>Auszufuehrende Aktion bei diesem Kommando. Moegliche Werte:
<text><table>
<rowh>
<col>Wert</col>
<col>Beschreibung</col>
</rowh>
<row>
<col>None</col>
<col>Keine Aktion.</col>
</row>
<row>
<col>Script (Standardwert)</col>
<col>Ausfuehrung des Scriptbefehls <em>PlayerControl</em>. Siehe <emlink href="playercontrols.xml#Script">Script-Callbacks</emlink>.</col>
</row>
<row>
<col>Menu</col>
<col>Oeffnen des Spielermenues (asynchrones Kommando).</col>
</row>
<row>
<col>MenuOK</col>
<col>Bestaetigen des ausgewaehlten Elementes im Spielermenue (asynchrones Kommando).</col>
</row>
<row>
<col>MenuCancel</col>
<col>Schliessen des Spielermenues (asynchrones Kommando).</col>
</row>
<row>
<col>MenuLeft / MenuUp / MenuRight / MenuDown</col>
<col>Navigation im Spielermenu (asynchrones Kommando).</col>
</row></table></text>
</col>
</row></table></text></text>
<h id="ControlSets">Sektion [ControlSets]</h>
<text>Definition von Standard-Steuerungsbelegungen.
<text><table>
<caption id="ControlSet">Beliebig viele Sektionen [ControlSet]</caption>
<rowh>
<col>Wert</col>
<col>Datentyp</col>
<col>Beschreibung</col>
</rowh>
<row>
<col>Name</col>
<col>Zeichenfolge</col>
<col>Interner Name zur Identifikation gleicher Steuerungsbelegungen. Die Namen der Standardbelegungen sind <em>Keyboard1</em>, <em>Keyboard1Classic</em>, <em>Keyboard2</em>, <em>Keyboard2Classic</em>, <em>Gamepad</em>. Ueber Platzhalter (*) koennen Tasten direkt in mehreren Belegungen definiert werden**.</col>
</row></table>
<text><table>
<caption id="ControlSet">Beliebig viele Sektionen [Assignment]</caption>
<rowh>
<col>Wert</col>
<col>Datentyp</col>
<col>Beschreibung</col>
</rowh>
<row>
<col>Key</col>
<col>Zeichenfolge</col>
<col>Taste(n) dieser Belegung oder Referenz auf eine andere Belegung. Siehe <emlink href="playercontrols.xml#Keys">Tastenbelegungen</emlink>.</col>
</row>
<row>
<col>ComboIsSequence</col>
<col>Boolean</col>
<col>Wenn wahr, werden mehrfache Tasten als Sequenz interpretiert. Das heisst, sie muessen nacheinander statt gleichzeitig gedrueckt werden. Siehe <emlink href="playercontrols.xml#Keys">Tastenbelegungen</emlink>.</col>
</row>
<row>
<col>Control</col>
<col>Zeichenfolge</col>
<col>Kommando, das mit dieser Belegung verknuepft wird. Der Name sollte dem <em>Identifier</em> eines in einer <emlink href="playercontrols.xml#ControlDef">[ControlDef]</emlink> definierten Kommandos entsprechen.</col>
</row>
<row>
<col>Priority</col>
<col>Integer</col>
<col>Prioritaet der Belegung. Nutzen mehrere Belegungen die gleichen Tasten, so wird zunaechst die Taste mit der hoeheren Prioritaet ausgefuehrt, bis ein Kommando als behandelt gilt.</col>
</row>
<row>
<col>TriggerMode</col>
<col>Bitmaske</col>
<col>Ausloesmodus dieser Belegung. Bitmaske aus folgenden Werten:
<text><table>
<rowh>
<col>Wert</col>
<col>Beschreibung</col>
</rowh>
<row>
<col>Default (Standardwert)</col>
<col>Keine besondere Aktion.</col>
</row>
<row>
<col>Hold</col>
<col>Die Taste versetzt das verlinkte Kommando in den gedrueckten Zustand, selbst wenn die Taste selbst nur angeschlagen wird. Nur gueltig, wenn das Kommando das <em>Hold</em>-Attribut gesetzt hat. Dieser Zustand bleibt erhalten, bis eine entsprechende Belegung mit Ausloesemodus <em>Release</em> gedrueckt wird. Siehe <emlink href="playercontrols.xml#Hold">Gehaltene Tasten</emlink>.</col>
</row>
<row>
<col>Release</col>
<col>Die Taste entfernt den gedrueckten Zustand. Eine Taste kann auch sowohl Hold als auch Release setzen, um zwischen den Zustaenden hin und her zu schalten. Siehe <emlink href="playercontrols.xml#Hold">Gehaltene Tasten</emlink>.</col>
</row>
<row>
<col>AlwaysUnhandled</col>
<col>Der Tastendruck wird immer an die Belegung mit der naechstniedrigen Prioritaet weitergereicht, unabhaengig davon, ob das vorherige Kommando erfolgreich ausgefuehrt wurde.</col>
</row>
<row>
<col>ToggleUnhandled</col>
<col>Der Tastendruck wird genau dann an die Belegung mit der naechstniedrigen Prioritaet weitergereicht, wenn das vorherige Kommando erfolgreich ausgefuehrt wurde. Hiermit lassen sich Makros definieren. **</col>
</row></table></text>
</col>
</row></table></text>
</text></text>
</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, 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]
[ControlDef]
Identifier=Jump
GUIName=Jump
GUIDesc=Hoppin' around
Repeat=5
[ControlSets]
[ControlSet]
Name=Keyboard1
[Assignment]
Key=W
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, bool release)
{
// Welches Kommando wurde ausgeloest?
// Die Konstante CON_Jump wurde automatisch durch die Definition in PlayerControls.txt angelegt
if (control == CON_Jump &amp;&amp; !release)
{
// Sprungtaste gedrueckt. Der vom Spieler ausgewaehlte Clonk soll springen
var player_clonk = GetCursor(player);
if (player_clonk &amp;&amp; player_clonk->Jump())
{
// Das Kommando wurde erfolgreich abgearbeitet
return true;
}
}
// Unbekanntes Kommando
return false;
}</code>
<h id="ExtraData">ExtraData</h>
<text>Da nicht jede Objektdefinition die globale PlayerControl-Funktion ueberladen kann, gibt es das ExtraData-Feld zum Verteilen von Kommandos. Zum Beispiel fuer folgende Definition:</text>
<code>[ControlDefs]
[ControlDef]
Identifier=Dig
GUIName=Dig
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, bool release)
{
// Behandlung bekannter Befehle
// [...]
// Befehl mit eigener Behandlung
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, bool release)
{
// Behandlung bekannter Befehle
// Grabkommando direkt an die Schaufel
if (control == CON_Dig)
{
// Nur, wenn ein Clonk ausgewaehlt ist, der graben kann
var player_clonk = GetCursor(player);
if (player_clonk &amp;&amp; player_clonk->HasShovel())
{
return player_clonk->StartDig();
}
}
// Unbekanntes Kommando
return false;
}</code>
<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 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>
</ul></text>
<text>Bestes Beispiel hierfuer ist ein Richtungskommando:</text>
<code> [ControlDef]
Identifier=Left
GUIName=Left
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, bool release)
{
if (control == CON_Left) return UpdateControlDir(player);
// ...
}
global func UpdateControlDir(int player)
{
// Clonk ausgewaehlt?
var player_clonk = GetCursor(player);
if (player_clonk)
{
// Clonkrichtung aktualisieren
var new_comdir = COMD_Stop;
if (GetPlayerControlState(player, CON_Left)) new_comdir = COMD_Left;
player_clonk->SetComDir(new_comdir);
// Kommando behandelt
return true;
}
// Kommando behandelt
return false;
}</code>
<text>Um klassisches Steuerungsverhalten zu erreichen, kann eine Tastenbelegung den <em>Hold</em>-Zustand emulieren:</text>
<code> [Assignment]
Key=A
Control=Left
TriggerMode=Hold
[Assignment]
Key=S
Control=Left
TriggerMode=Release | AlwaysUnhandled</code>
<h id="Globals">Globale Definitionen</h>
<text>...</text>
<h id="Repeat">Tastenwiederholungen</h>
<text>Hat ein Kommando ein <em>RepeatDelay</em> definiert, so werden wiederholte Kommandos beim Halten der Taste erzeugt. Zum Beispiel fuer ein Wurkommando:</text>
<code> [ControlDef]
Identifier=Throw
GUIName=Throw
GUIDesc=Get rid of your selected inventory
Hold=1
RepeatDelay=5
InitialRepeatDelay=35</code>
<text>Im Beispiel koennte man die Wurftaste nach einmal Druecken auch halten. Das Wurfkommando wuerde dann nach 35 Frames (ca. eine Sekunde) halten alle 5 Frames automatisch wiederholt.</text>
<text>Wiederholungen werden nur erzeugt, wenn das Kommando ebenfalls das <em>Hold</em>-Flag gesetzt hat.</text>
<h id="Deactivate">Deaktivierte Kommandos**</h>
<text>...</text>
<h id="Keys">Tastenbelegungen</h>
<text>...</text>
<h id="Priority">Prioritaeten</h>
<text>...</text>
</part>
<text><em>** - noch nicht implementiert</em></text>
<author>Sven2</author><date>Juni 2009</date>
</doc>

View File

@ -2,12 +2,6 @@
#strict 2
// Zauberei - benötigt, wenn der Clonk Zielzauber z.B. aus dem Zauberturm zaubert
// Auch benötigt für den König
local pAimer; // Aktive Zielsteuerung; wird abgrbrochen, wenn der Zauberer gestört wird (Nur Fantasypack)
local pAimedSpell; // Zauber, der gezielt wird (Nur Fantasypack)
local pAimedSpellOrigin; // Objekt, das einen Zielzauber initiiert hat. An dieses werden SpellFailed/SpellSucceeded-Nachrichten weitergeleitet
/* Initialisierung */
@ -68,82 +62,78 @@ public func FindTree()
return FindObject(Find_AtPoint(), Find_OCF(OCF_Chop), Find_Layer(GetObjectLayer()));
}
/* Steuerung */
protected func ControlLeft()
public func GetInteractionTarget()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlLeft")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlLeft(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlLeftDouble()
// Contained interaction target
var container = Contained();
if (container)
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlLeftDouble")) return true;
if (container->GetCategory() & (C4D_Structure | C4D_Vehicle)) return container;
}
// Procedure interaction target
// (Except for FIGHT, of course. You can't control your enemy ;))
var proc = GetProcedure();
if (proc == "PUSH" || proc == "PULL" || proc == "BUILD") return GetActionTarget();
// First contents object interaction target
return Contents(0);
}
protected func ControlRightDouble()
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
{
// Generic movement
if (inherited(plr, ctrl, x, y, strength, repeat, release)) return true;
var proc = GetProcedure();
// Handled by InteractionTarget?
var interaction_target = GetInteractionTarget();
if (interaction_target)
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlRightDouble")) return true;
if (interaction_target->ObjectControl(plr, ctrl, x,y, strength, repeat, release)) return true;
}
protected func ControlLeftReleased()
{
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlLeftReleased(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlRight()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlRight")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlRight(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlRightReleased()
{
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlRightReleased(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlUp()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlUp")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlUp(this);
// Bei JnR Delfinsprung
if(GetPlrCoreJumpAndRunControl(GetController()))
DolphinJump();
// Keine überladene Steuerung
return 0;
}
protected func ControlUpReleased()
{
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlUpReleased(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlUpDouble()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlUpDouble")) return 1;
DolphinJump();
// Dolphin jump
if (ctrl == CON_DolphinJump) return DolphinJump();
// Context menu
else if (ctrl == CON_Context)
{
// Context menu of interaction target (fallback to this if no interaction target)
if (!interaction_target) interaction_target = this;
SetCommand(this,"Context",0,0,0,interaction_target);
return ExecuteCommand();
}
// Throw
else if (ctrl == CON_Throw)
{
// During Scale+Hangle, this means "Drop". During dig, this means object dig out request. Otherwise, throw.
if (proc == "DIG")
return SetActionData(!GetActionData());
else if (proc == "SCALE" || proc == "HANGLE")
return PlayerObjectCommand(plr, false, "Drop");
else
return PlayerObjectCommand(plr, false, "Throw");
}
// Dig
else if (ctrl == CON_Dig)
{
if (proc == "DIG")
{
// Currently, another press on dig ends digging. Maybe changed once we have the shovel system?
SetAction("Walk");
return true;
}
else if (proc == "WALK")
{
if (!GetPhysical("Dig")) return false;
if (!SetAction("Dig")) return false;
SetActionData(0);
return true;
}
// Can't dig now
return false;
}
// Unhandled
return false;
}
private func DolphinJump()
@ -164,105 +154,6 @@ private func DolphinJump()
SetAction("Dive");
}
protected func ControlDown()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlDown")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDown(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDownReleased()
{
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDownReleased(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDownSingle()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlDownSingle")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDownSingle(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDownDouble()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlDownDouble")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDownDouble(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDig()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlDig")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDig(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDigReleased()
{
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDigReleased(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDigSingle()
{
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDigSingle(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlDigDouble()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlDigDouble")) return 1;
// Steuerung an Pferd weiterleiten
if (IsRiding()) return GetActionTarget()->~ControlDigDouble(this);
// Keine überladene Steuerung
return 0;
}
protected func ControlThrow()
{
// Bei vorherigem Doppel-Stop nur Ablegen
if (GetPlrDownDouble(GetOwner())) return 0;
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlThrow")) return 1;
// Reiten und Werfen
if (IsRiding())
if (Contents(0))
{
SetAction("RideThrow");
return 1;
}
// Keine überladene Steuerung
return 0;
}
protected func ControlUpdate(object self, int comdir, bool dig, bool throw)
{
// Steuerung an Pferd weiterleiten
if(IsRiding()) return GetActionTarget()->~ControlUpdate(self, comdir, dig, throw);
// Keine überladene Steuerung
return 0;
}
protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data)
{
// Kommando MoveTo an Pferd weiterleiten
@ -287,6 +178,9 @@ protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data)
return 0;
}
public func ControlDownDouble() {} // dummy
/* Verwandlung */
private func RedefinePhysical(szPhys, idTo)
@ -396,6 +290,7 @@ public func Redefine(idTo)
return 1;
}
/* Essen */
public func Feed(iLevel)
@ -405,6 +300,7 @@ public func Feed(iLevel)
return 1;
}
/* Aktionen */
private func Riding()
@ -497,6 +393,7 @@ protected func Scaling()
return 1;
}
/* Ereignisse */
protected func CatchBlow()
@ -538,9 +435,6 @@ protected func Death(int iKilledBy)
// Der Broadcast könnte seltsame Dinge gemacht haben: Clonk ist noch tot?
if (GetAlive()) return;
// den Beutel fallenlassen
if(GetAlchemBag()) GetAlchemBag()->~Loose();
Sound("Die");
DeathAnnounce();
// Letztes Mannschaftsmitglied tot: Script benachrichtigen
@ -579,6 +473,7 @@ protected func CheckStuck()
SetPosition(GetX(), GetY() + 1);
}
/* Status */
public func IsRiding()
@ -589,6 +484,7 @@ public func IsRiding()
public func IsClonk() { return 1; }
/* Kontext */
public func ContextRelease(pCaller)
@ -640,6 +536,7 @@ public func ContextHome(pCaller)
return 1;
}
/* Hilfsfunktion */
public func ContainedCall(string strFunction, object pTarget)
@ -649,34 +546,6 @@ public func ContainedCall(string strFunction, object pTarget)
AddCommand(this, "Enter", pTarget);
}
/* Steuerung */
protected func ControlSpecial2()
{
[$CtrlMenuDesc$|Image=CXTX]
// In einem Gebäude oder Fahrzeug: das Kontextmenü des Gebäudes öffnen
if (Contained())
if ((Contained()->GetCategory() & C4D_Structure) || (Contained()->GetCategory() & C4D_Vehicle))
{
SetCommand(this,"Context",0,0,0,Contained());
return ExecuteCommand();
}
// Fasst ein Objekt an: Kontextmenü des angefassten Objekts öffnen
if (GetAction() == "Push")
{
SetCommand(this,"Context",0,0,0,GetActionTarget());
return ExecuteCommand();
}
// Trägt ein Objekt: Kontextmenü des ersten getragenen Objekts öffnen
if (Contents(0))
{
SetCommand(this,"Context",0,0,0,Contents(0));
return ExecuteCommand();
}
// Ansonsten das Kontextmenü des Clonks öffnen
SetCommand(this,"Context",0,0,0,this);
return ExecuteCommand();
}
/* Callback beim Auswahl aus dem Construct-Kontextmenu */
@ -688,6 +557,7 @@ public func ControlCommandConstruction(target, x, y, target2, def)
return FinishCommand(false, 0) ;
}
/* Automatische Produktion */
public func ControlCommandAcquire(target, x, y, target2, def)
@ -747,6 +617,7 @@ public func GetProducerOf(def)
return FindObject(Find_InRect(-500,-250,1000,500), Find_Func("IsProducerOf", this, def), Sort_Distance());
}
/* Trinken */
public func Drink(object pDrink)
@ -758,6 +629,7 @@ public func Drink(object pDrink)
// die Potions löschen sich meist selber...
}
/* Einsammeln */
public func RejectCollect(id idObject, object pObject)
@ -784,6 +656,7 @@ public func RejectCollect(id idObject, object pObject)
return GetNonSpecialCount()>=MaxContentsCount();
}
/* Itemlimit */
public func MaxContentsCount() { return 1; }
@ -880,24 +753,6 @@ public func DescendVehicle()
}
/* Effektsteuerung */
private func Control2Effect(string szControl)
{
// Von Effektzahl abwärts zählen
var i = GetEffectCount(nil, this), iEffect;
var res;
while (i--)
{
// Effekte mit Control im Namen benachrichtigen
iEffect = GetEffect("*Control*", this, i);
// Message("%s", this, GetEffect(0, this, iEffect, 1));
if ( GetEffect(nil, this, iEffect, 1) )
res += EffectCall(this, iEffect, szControl);
}
return res;
}
/* Pfeile */
@ -969,40 +824,11 @@ private func GetArrowCount()
}
/* Zauberei - benötigt, wenn der Clonk Zielzauber z.B. aus dem Zauberturm zaubert */
public func SpellFailed(id idSpell, object pByCaller)
{
// Umleiten an eigentliche Zauberquelle? (Buch, Zauberturm, etc.)
var pSpellOrigin = pAimedSpellOrigin;
pAimedSpellOrigin = 0;
if (pSpellOrigin && pSpellOrigin != this)
// Auch bei nicht erfolgreicher Umleitung abbrechen: Das zaubernde Objekt hat im Normalfall die Zutaten/Zauberenergie für den
// Zauber bereit gestellt, und diese sollten nicht an den Clonk zurück gegeben werden
return (pSpellOrigin->~SpellFailed(idSpell, this));
// Magieenergie zurückgeben
DoMagicEnergy(idSpell->GetDefValue(), true);
// Alchemische Zutaten zurückgeben
if(ObjectCount(ALCO)) IncreaseAlchem(idSpell);
}
public func SpellSucceeded(id idSpell, object pByCaller)
{
// Umleiten an eigentliche Zauberquelle? (Buch, Zauberturm, etc.)
var pSpellOrigin = pAimedSpellOrigin;
pAimedSpellOrigin = 0;
if (pSpellOrigin && pSpellOrigin != this)
// Auch bei nicht erfolgreicher Umleitung abbrechen: Das zaubernde Objekt hat im Normalfall das Magietraining schon erledigt
return (pSpellOrigin->~SpellSucceeded(idSpell, this));
// Globaler Aufruf für Zauber
OnClonkSucceededSpell(idSpell);
}
// Der Clonk kann von sich aus nicht zaubern und hat keine Aktivitäten dafür
private func SetMagicAction(id idForSpell) {}
private func SetCastAction() {}
private func EndMagicAction() {}
/* Dummies, damit die Engine die Namen kennt... */
func Activate() {}
func HowToProduce() {}
func PackCount() {}
func Definition(def) {
SetProperty("ActMap", {
Walk = {

View File

@ -33,12 +33,14 @@ global func ShowNeededMaterial(object pOfObject)
MessageWindow(GetNeededMatStr(pOfObject), GetOwner(),CXCN,pOfObject->GetName());
return 1;
}
// Fasskonfiguration
// Kann z.B. durch eine Spielregel überladen werden (Shamino)
// Bit 0 (1): Wasserfässer sind auch im Verkauf 8 Clunker wert
// Bit 1 (2): Fässer werden beim Verkaufen nicht entleert (sind wieder voll kaufbar)
// Bit 2 (4): Nur Wasserfässer werden beim Verkaufen nicht entleert (sind wieder voll kaufbar)
global func GetBarrelType() {}
func BarrelDoFill() {}
func BarrelIsFull() {}
global func BarrelConfiguration() { return 5; }
/// Removes a material pixel from the specified location, if the material is flammable.
@ -53,6 +55,7 @@ global func FlameConsumeMaterial(int x, int y)
return !!ExtractMaterialAmount(x, y, mat, 1);
}
/// 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.

View File

@ -0,0 +1,228 @@
#strict 2
// 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, %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)
{
// Object controlled by plr
cursor->SetController(plr);
// Overload by effect?
if (cursor->Control2Effect(plr, ctrl, x, y, strength, repeat, release)) return true;
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!
// Control2Effect
// Call control function in all effects that have "Control" in their name
global func Control2Effect(int ctrl, int x, int y, int strength, bool repeat, bool release)
{
// Count down from EffectCount, in case effects get deleted
var i = GetEffectCount("*Control*", this), iEffect;
var res;
while (i--)
{
iEffect = GetEffect("*Control*", this, i);
if ( GetEffect(0, this, iEffect, 1) )
if (EffectCall(this, iEffect, "Control", ctrl, x,y,strength, repeat, release))
return true;
}
// No effect handled the control
return false;
}
// ObjectControl
// Called from PlayerControl when a control is issued to the cursor
// Return whether handled
// To be overloaded by specific objects to enable additional controls
global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
{
// 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
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")
// Only horizontal movement changed actual direction
// Also, enfore clear Left/Right commands without leftover Up/Down
// CON_Down is never handled this way, thus forcing a CON_Stop
is_handled = (old_cx != new_cx) && !new_cy;
else if (proc == "SCALE")
// Only vertical movement changed actual direction
// Also, enfore clear Up/Down to prevent "Zuppel" in corner
is_handled = (old_cy != new_cy) && !new_cx;
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;
}
// 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)
{
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

@ -0,0 +1,222 @@
[ControlDefs]
[ControlDef]
Identifier=None
[ControlDef]
Identifier=Left
GUIName=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
[ControlDef]
Identifier=Throw
GUIName=Throw
GUIDesc=Get rid of your selected inventory
Hold=1
RepeatDelay=5
InitialRepeatDelay=35
[ControlDef]
Identifier=Dig
GUIName=Dig
GUIDesc=Delve through the earth
[ControlDef]
Identifier=DolphinJump
GUIName=Dolphin Jump
GUIDesc=Jump out of the water while swimming
[ControlDef]
Identifier=Context
GUIName=Context menu
GUIDesc=Open context menu of current interaction target
[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=CON_Left
Control=Right
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Left
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=CON_Left
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=D
Control=Right
TriggerMode=Hold
[Assignment]
Key=CON_Right
Control=Left
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Right
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=CON_Right
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=S
Control=Down
TriggerMode=Hold
[Assignment]
Key=CON_Down
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Down
Control=Left
TriggerMode=Release
Priority=-5
[Assignment]
Key=CON_Down
Control=Right
TriggerMode=Release
Priority=-5
[Assignment]
Key=CON_Down
Control=Down
TriggerMode=Release
Priority=-10
[Assignment]
Key=W
Control=Up
TriggerMode=Hold
[Assignment]
Key=CON_Up
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Up
Control=Left
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=CON_Up
Control=Right
TriggerMode=Release | AlwaysUnhandled
Priority=-5
[Assignment]
Key=CON_Dig
Control=Down
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Dig
Control=Left
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Dig
Control=Right
TriggerMode=Release | AlwaysUnhandled
Priority=5
[Assignment]
Key=CON_Dig
Control=Up
TriggerMode=Release | AlwaysUnhandled
Priority=5
[ControlSet]
Name=Keyboard2*
[Assignment]
Key=Q
Control=Throw
[Assignment]
Key=E
Control=Dig
[Assignment]
Key=Up,Up
ComboIsSequence=1
Control=DolphinJump
Priority=10
[Assignment]
Key=F
Control=Context

View File

@ -561,7 +561,7 @@ C4ApplicationGameTimer::C4ApplicationGameTimer()
void C4ApplicationGameTimer::SetGameTickDelay(uint32_t iDelay)
{
// Smaller than minimum refresh delay?
if (iDelay < Config.Graphics.MaxRefreshDelay)
if (iDelay < uint32_t(Config.Graphics.MaxRefreshDelay))
{
// Set critical timer
SetDelay(iDelay);
@ -584,7 +584,7 @@ bool C4ApplicationGameTimer::Execute(int iTimeout, pollfd *)
{
// Check timer and reset
if (!CheckAndReset()) return true;
int Now = timeGetTime();
unsigned int Now = timeGetTime();
// Execute
if(Now >= iLastGameTick + iGameTickDelay || Game.GameGo)
{

View File

@ -57,6 +57,7 @@
#include <C4ObjectMenu.h>
#include <C4GameLobby.h>
#include <C4ChatDlg.h>
#include <C4PlayerControl.h>
#include <C4MouseControl.h>
#include <C4PXS.h>
#include <C4MessageInput.h>
@ -594,6 +595,8 @@ void C4Game::Clear()
KeyboardInput.Clear();
SetMusicLevel(100);
PlayList.Clear();
PlayerControlAssignmentSets.Clear();
PlayerControlDefs.Clear();
// global fullscreen class is not cleared, because it holds the carrier window
// but the menu must be cleared (maybe move Fullscreen.Menu somewhere else?)
@ -1830,7 +1833,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));
@ -1861,24 +1864,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()));
}
}
@ -2274,6 +2283,9 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky)
{ LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
SetInitProgress(57);
// Final init for loaded player commands. Before linking scripts, so CON_* constants are registered
PlayerControlDefs.FinalInit();
// Link scripts
if (!LinkScriptEngine()) return false;
SetInitProgress(58);
@ -2443,9 +2455,6 @@ bool C4Game::InitScriptEngine()
{ LogFatal(LoadResStr("IDS_ERR_INVALIDSYSGRP")); return false; }
C4Group &File = Application.SystemGroup;
// Load string table
MainSysLangStringTable.LoadEx("StringTbl", File, C4CFN_ScriptStringTbl, Config.General.LanguageEx);
// get scripts
char fn[_MAX_FNAME+1] = { 0 };
File.ResetSearch();
@ -3161,37 +3170,6 @@ bool C4Game::InitKeyboard()
KeyboardInput.RegisterKey(new C4CustomKey(C4KeyCodeEx(KEY_Default ), "NetAllowJoinToggle", KEYSCOPE_Generic, new C4KeyCB <C4Network2> (Network, &C4Network2::ToggleAllowJoin)));
KeyboardInput.RegisterKey(new C4CustomKey(C4KeyCodeEx(KEY_Default ), "NetStatsToggle", KEYSCOPE_Generic, new C4KeyCB <C4GraphicsSystem>(GraphicsSystem, &C4GraphicsSystem::ToggleShowNetStatus)));
// Map player keyboard controls
int32_t iKdbSet,iCtrl;
StdStrBuf sPlrCtrlName;
for (iKdbSet=C4P_Control_Keyboard1; iKdbSet<=C4P_Control_Keyboard4; iKdbSet++)
for (iCtrl=0; iCtrl<C4MaxKey; iCtrl++)
{
sPlrCtrlName.Format("Kbd%dKey%d", iKdbSet-C4P_Control_Keyboard1+1, iCtrl+1);
KeyboardInput.RegisterKey(new C4CustomKey(
C4KeyCodeEx(Config.Controls.Keyboard[iKdbSet][iCtrl]),
sPlrCtrlName.getData(), KEYSCOPE_Control,
new C4KeyCBExPassKey<C4Game, C4KeySetCtrl>(*this, C4KeySetCtrl(iKdbSet, iCtrl), &C4Game::LocalControlKey, &C4Game::LocalControlKeyUp),
C4CustomKey::PRIO_PlrControl));
}
// Map player gamepad controls
int32_t iGamepad;
for (iGamepad=C4P_Control_GamePad1; iGamepad<=C4P_Control_GamePad1+C4ConfigMaxGamepads; iGamepad++)
{
C4ConfigGamepad &cfg = Config.Gamepads[iGamepad-C4P_Control_GamePad1];
for (iCtrl=0; iCtrl<C4MaxKey; iCtrl++)
{
if (cfg.Button[iCtrl] == -1) continue;
sPlrCtrlName.Format("Joy%dBtn%d", iGamepad-C4P_Control_GamePad1+1, iCtrl+1);
KeyboardInput.RegisterKey(new C4CustomKey(
C4KeyCodeEx(cfg.Button[iCtrl]),
sPlrCtrlName.getData(), KEYSCOPE_Control,
new C4KeyCBExPassKey<C4Game, C4KeySetCtrl>(*this, C4KeySetCtrl(iGamepad, iCtrl), &C4Game::LocalControlKey, &C4Game::LocalControlKeyUp),
C4CustomKey::PRIO_PlrControl));
}
}
// load any custom keysboard overloads
KeyboardInput.LoadCustomConfig();
@ -3238,12 +3216,35 @@ bool C4Game::InitSystem()
// init keyboard input (default keys, plus overloads)
if (!InitKeyboard())
{ LogFatal(LoadResStr("IDS_ERR_NOKEYBOARD")); return false; }
// Load string table
UpdateLanguage();
// Player keyboard input: Key definitions and default sets
if (!InitPlayerControlSettings()) return false;
// Rank system
::DefaultRanks.Init(Config.GetSubkeyPath("ClonkRanks"), LoadResStr("IDS_GAME_DEFRANKS"), 1000);
// done, success
return true;
}
void C4Game::UpdateLanguage()
{
// Reload System.c4g string table
MainSysLangStringTable.LoadEx("StringTbl", Application.SystemGroup, C4CFN_ScriptStringTbl, Config.General.LanguageEx);
}
bool C4Game::InitPlayerControlSettings()
{
C4PlayerControlFile PlayerControlFile;
if (!PlayerControlFile.Load(Application.SystemGroup, C4CFN_PlayerControls, &MainSysLangStringTable)) { LogFatal("[!]Error loading player controls"); return false; }
PlayerControlDefs = PlayerControlFile.GetControlDefs();
PlayerControlAssignmentSets.Clear();
PlayerControlAssignmentSets.MergeFrom(PlayerControlFile.GetAssignmentSets(), false);
PlayerControlAssignmentSets.ResolveRefs(&PlayerControlDefs);
// And overwrites from config
//PlayerControlAssignmentSets.MergeFrom(Config.Controls.Assignments);
return true;
}
C4Player *C4Game::JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo)
{
assert(pInfo);
@ -3275,71 +3276,6 @@ void C4Game::FixRandom(int32_t iSeed)
Randomize3();
}
bool C4Game::LocalControlKey(C4KeyCodeEx key, C4KeySetCtrl Ctrl)
{
// keyboard callback: Perform local player control
C4Player *pPlr;
if (pPlr = Players.GetLocalByKbdSet(Ctrl.iKeySet))
{
// Swallow a event generated from Keyrepeat for AutoStopControl
if (pPlr->PrefControlStyle)
{
if (key.IsRepeated())
return true;
}
LocalPlayerControl(pPlr->Number,Control2Com(Ctrl.iCtrl, false));
return true;
}
// not processed - must return false here, so unused keyboard control sets do not block used ones
return false;
}
bool C4Game::LocalControlKeyUp(C4KeyCodeEx key, C4KeySetCtrl Ctrl)
{
// Direct callback for released key in AutoStopControl-mode (ignore repeated)
if (key.IsRepeated())
return true;
C4Player *pPlr;
if ((pPlr = Players.GetLocalByKbdSet(Ctrl.iKeySet)) && pPlr->PrefControlStyle)
{
int iCom = Control2Com(Ctrl.iCtrl, true);
if (iCom != COM_None) LocalPlayerControl(pPlr->Number, iCom);
return true;
}
// not processed - must return false here, so unused keyboard control sets do not block used ones
return false;
}
void C4Game::LocalPlayerControl(int32_t iPlayer, int32_t iCom)
{
C4Player *pPlr = Players.Get(iPlayer); if (!pPlr) return;
int32_t iData=0;
// Menu button com
if (iCom==COM_PlayerMenu)
{
// Player menu open: close
if (pPlr->Menu.IsActive())
pPlr->Menu.Close(false);
// Menu closed: open main menu
else
pPlr->ActivateMenuMain();
return;
}
// Local player menu active: convert menu com and control local
if (pPlr->Menu.ConvertCom(iCom,iData,true))
{
pPlr->Menu.Control(iCom,iData);
return;
}
// Pre-queue asynchronous menu conversions
if (pPlr->Cursor && pPlr->Cursor->Menu)
pPlr->Cursor->Menu->ConvertCom(iCom,iData,true);
// Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!)
if (pPlr->Eliminated) return;
// Player control: add to control queue
Input.Add(CID_PlrControl, new C4ControlPlayerControl(iPlayer,iCom,iData));
}
bool C4Game::DefinitionFilenamesFromSaveGame()
{
const char *pSource;

View File

@ -37,9 +37,10 @@
#include "C4Scoreboard.h"
#include <C4VideoPlayback.h>
#include <C4ScriptHost.h>
#include <C4PlayerControl.h>
class C4Game
{
{
private:
// used as StdCompiler-parameter
struct CompileSettings
@ -60,8 +61,8 @@ class C4Game
C4KeySetCtrl(int32_t iKeySet, int32_t iCtrl) : iKeySet(iKeySet), iCtrl(iCtrl) {}
};
public:
C4Game();
public:
C4Game();
~C4Game();
public:
C4ClientList &Clients; // Shortcut
@ -70,7 +71,7 @@ class C4Game
C4PlayerInfoList &PlayerInfos; // Shortcut
C4PlayerInfoList &RestorePlayerInfos; // Shortcut
C4RoundResults RoundResults;
C4Scenario C4S;
C4Scenario C4S;
C4ComponentHost Info;
C4ComponentHost Title;
C4ComponentHost Names;
@ -82,12 +83,14 @@ class C4Game
C4PathFinder PathFinder;
C4TransferZones TransferZones;
C4Group ScenarioFile;
C4Group ScenarioFile;
C4GroupSet GroupSet;
C4Group *pParentGroup;
C4Extra Extra;
C4ScenarioSection *pScenarioSections, *pCurrentScenarioSection;
C4Effect *pGlobalEffects;
C4PlayerControlDefs PlayerControlDefs;
C4PlayerControlAssignmentSets PlayerControlAssignmentSets;
#ifndef USE_CONSOLE
// We don't need fonts when we don't have graphics
C4FontLoader FontLoader;
@ -99,8 +102,8 @@ class C4Game
class C4FileMonitor *pFileMonitor;
class C4GameSec1Timer *pSec1Timer;
char CurrentScenarioSection[C4MaxName+1];
char ScenarioFilename[_MAX_PATH+1];
char CurrentScenarioSection[C4MaxName+1];
char ScenarioFilename[_MAX_PATH+1];
StdCopyStrBuf ScenarioTitle;
char PlayerFilenames[20*_MAX_PATH+1];
char DefinitionFilenames[20*_MAX_PATH+1];
@ -109,13 +112,13 @@ class C4Game
int32_t StartupPlayerCount;
int32_t FPS,cFPS;
int32_t HaltCount;
bool GameOver;
bool Evaluated;
bool GameOver;
bool Evaluated;
bool GameOverDlgShown;
bool fScriptCreatedObjects;
bool fLobby;
int32_t iLobbyTimeout;
bool fObserve;
bool fObserve;
bool fReferenceDefinitionOverride;
bool NetworkActive;
bool Record;
@ -149,24 +152,21 @@ class C4Game
// next mission to be played after this one
StdCopyStrBuf NextMission, NextMissionText, NextMissionDesc;
public:
// Init and execution
public:
// Init and execution
void Default();
void Clear();
void Abort(bool fApproved = false); // hard-quit on Esc+Y (/J/O)
void Evaluate();
void Evaluate();
void ShowGameOverDlg();
bool DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool fAlt, bool fCtrl, bool fShift, bool fRepeated, class C4GUI::Dialog *pForDialog=NULL, bool fPlrCtrlOnly=false);
void DrawCursors(C4TargetFacet &cgo, int32_t iPlayer);
bool LocalControlKey(C4KeyCodeEx key, C4KeySetCtrl Ctrl);
bool LocalControlKeyUp(C4KeyCodeEx key, C4KeySetCtrl Ctrl);
void LocalPlayerControl(int32_t iPlayer, int32_t iCom);
void FixRandom(int32_t iSeed);
void FixRandom(int32_t iSeed);
bool Init();
bool PreInit();
void ParseCommandLine(const char *szCmdLine);
void ParseCommandLine(const char *szCmdLine);
bool Execute();
class C4Player *JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo);
class C4Player *JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo);
bool DoGameOver();
bool CanQuickSave();
bool QuickSave(const char *strFilename, const char *strTitle, bool fForceSave=false);
@ -181,10 +181,10 @@ class C4Game
bool IsPaused();
// Network
void Synchronize(bool fSavePlayerFiles);
void SyncClearance();
void SyncClearance();
bool ReSync();
void SyncCheckFiles(); // check if files are in sync
// Editing
// Editing
bool DropFile(const char *szFilename, float iX, float iY);
bool CreateViewport(int32_t iPlayer, bool fSilent=false);
bool DropDef(C4ID id, float iX, float iY);
@ -192,49 +192,49 @@ class C4Game
bool ReloadFile(const char *szPath);
bool ReloadDef(C4ID id);
bool ReloadParticle(const char *szName);
// Object functions
// Object functions
void ClearPointers(C4PropList *cobj);
C4Object *CreateObject(C4PropList * type, C4Object *pCreator, int32_t owner=NO_OWNER,
int32_t x=50, int32_t y=50, int32_t r=0,
FIXED xdir=Fix0, FIXED ydir=Fix0, FIXED rdir=Fix0, int32_t iController=NO_OWNER);
C4Object *CreateObject(C4ID type, C4Object *pCreator, int32_t owner=NO_OWNER,
int32_t x=50, int32_t y=50, int32_t r=0,
FIXED xdir=Fix0, FIXED ydir=Fix0, FIXED rdir=Fix0, int32_t iController=NO_OWNER);
C4Object *CreateObject(C4ID type, C4Object *pCreator, int32_t owner=NO_OWNER,
int32_t x=50, int32_t y=50, int32_t r=0,
FIXED xdir=Fix0, FIXED ydir=Fix0, FIXED rdir=Fix0, int32_t iController=NO_OWNER);
C4Object *CreateObjectConstruction(C4PropList * type,
C4Object *pCreator,
int32_t owner,
int32_t ctx=0, int32_t bty=0,
C4Object *pCreator,
int32_t owner,
int32_t ctx=0, int32_t bty=0,
int32_t con=1, bool terrain=false);
C4Object *CreateInfoObject(C4ObjectInfo *cinf, int32_t owner,
int32_t tx=50, int32_t ty=50);
void BlastObjects(int32_t tx, int32_t ty, int32_t level, C4Object *inobj, int32_t iCausedBy, C4Object *pByObj);
C4Object *CreateInfoObject(C4ObjectInfo *cinf, int32_t owner,
int32_t tx=50, int32_t ty=50);
void BlastObjects(int32_t tx, int32_t ty, int32_t level, C4Object *inobj, int32_t iCausedBy, C4Object *pByObj);
void ShakeObjects(int32_t tx, int32_t ry, int32_t range);
C4Object *OverlapObject(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt,
int32_t category);
C4Object *FindObject(C4ID id,
int32_t iX=0, int32_t iY=0, int32_t iWdt=0, int32_t iHgt=0,
DWORD ocf=OCF_All,
const char *szAction=NULL, C4Object *pActionTarget=NULL,
C4Object *pExclude=NULL,
C4Object *pContainer=NULL,
int32_t iOwner=ANY_OWNER,
C4Object *pFindNext=NULL);
C4Object *OverlapObject(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt,
int32_t category);
C4Object *FindObject(C4ID id,
int32_t iX=0, int32_t iY=0, int32_t iWdt=0, int32_t iHgt=0,
DWORD ocf=OCF_All,
const char *szAction=NULL, C4Object *pActionTarget=NULL,
C4Object *pExclude=NULL,
C4Object *pContainer=NULL,
int32_t iOwner=ANY_OWNER,
C4Object *pFindNext=NULL);
C4Object *FindVisObject( // find object in view at pos, regarding parallaxity and visibility (but not distance)
float tx, float ty, int32_t iPlr, const C4Facet &fctViewport,
float iX=0, float iY=0, float iWdt=0, float iHgt=0,
DWORD ocf=OCF_All,
C4Object *pExclude=NULL,
int32_t iOwner=ANY_OWNER,
C4Object *pFindNext=NULL);
int32_t ObjectCount(C4ID id,
int32_t x=0, int32_t y=0, int32_t wdt=0, int32_t hgt=0,
DWORD ocf=OCF_All,
const char *szAction=NULL, C4Object *pActionTarget=NULL,
C4Object *pExclude=NULL,
C4Object *pContainer=NULL,
int32_t iOwner=ANY_OWNER);
C4Object *FindBase(int32_t iPlayer, int32_t iIndex);
C4Object *FindFriendlyBase(int32_t iPlayer, int32_t iIndex);
float tx, float ty, int32_t iPlr, const C4Facet &fctViewport,
float iX=0, float iY=0, float iWdt=0, float iHgt=0,
DWORD ocf=OCF_All,
C4Object *pExclude=NULL,
int32_t iOwner=ANY_OWNER,
C4Object *pFindNext=NULL);
int32_t ObjectCount(C4ID id,
int32_t x=0, int32_t y=0, int32_t wdt=0, int32_t hgt=0,
DWORD ocf=OCF_All,
const char *szAction=NULL, C4Object *pActionTarget=NULL,
C4Object *pExclude=NULL,
C4Object *pContainer=NULL,
int32_t iOwner=ANY_OWNER);
C4Object *FindBase(int32_t iPlayer, int32_t iIndex);
C4Object *FindFriendlyBase(int32_t iPlayer, int32_t iIndex);
C4Object *FindObjectByCommand(int32_t iCommand, C4Object *pTarget=NULL, C4Value iTx=C4VNull, int32_t iTy=0, C4Object *pTarget2=NULL, C4Object *pFindNext=NULL);
void CastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t level, int32_t tx, int32_t ty, int32_t iOwner=NO_OWNER, int32_t iController=NO_OWNER);
void BlastCastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t tx, int32_t ty, int32_t iController=NO_OWNER);
@ -248,8 +248,10 @@ class C4Game
bool SpeedUp();
bool SlowDown();
bool InitKeyboard(); // register main keyboard input functions
void UpdateLanguage();
bool InitPlayerControlSettings();
protected:
protected:
bool InitSystem();
void InitInEarth();
void InitVegetation();

View File

@ -525,7 +525,7 @@ void C4GraphicsSystem::SortViewportsByPlayerControl()
pPlr1 = ::Players.Get(pView->Player);
pPlr2 = ::Players.Get(pNext->Player);
// Swap order
if (pPlr1 && pPlr2 && ( LayoutOrder(pPlr1->Control) > LayoutOrder(pPlr2->Control) ))
if (pPlr1 && pPlr2 && ( LayoutOrder(pPlr1->ControlSet) > LayoutOrder(pPlr2->ControlSet) ))
{
if (pPrev) pPrev->Next = pNext; else FirstViewport = pNext;
pView->Next = pNext->Next;

View File

@ -203,6 +203,7 @@
#include "C4PathFinder.h"
#include "C4Physics.h"
#include "C4Player.h"
#include "C4PlayerControl.h"
#include "C4PlayerInfo.h"
#include "C4PlayerInfoListBox.h"
#include "C4PlayerList.h"

View File

@ -120,6 +120,7 @@
#define C4CFN_Teams "Teams.txt"
#define C4CFN_Parameters "Parameters.txt"
#define C4CFN_RoundResults "RoundResults.txt"
#define C4CFN_PlayerControls "PlayerControls.txt"
#define C4CFN_MapFolderData "FolderMap.txt"
#define C4CFN_MapFolderBG "FolderMap"

View File

@ -160,7 +160,7 @@ void C4ConfigGraphics::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(RenderInactiveEM, "RenderInactiveEM", 1 ));
pComp->Value(mkNamingAdapt(DisableGamma, "DisableGamma", 0 ,false, true));
pComp->Value(mkNamingAdapt(Monitor, "Monitor", 0 )); // 0 = D3DADAPTER_DEFAULT
pComp->Value(mkNamingAdapt(FireParticles, "FireParticles", true ));
pComp->Value(mkNamingAdapt(FireParticles, "FireParticles", 1 ));
pComp->Value(mkNamingAdapt(MaxRefreshDelay, "MaxRefreshDelay", 30 ));
pComp->Value(mkNamingAdapt(EnableShaders, "Shader", 0 ,false, true));
}

View File

@ -371,27 +371,45 @@ void C4ControlPlayerSelect::CompileFunc(StdCompiler *pComp)
C4ControlPacket::CompileFunc(pComp);
}
// *** C4ControlPlayerControl
void C4ControlPlayerControl::Execute() const
{
C4Player *pPlr=::Players.Get(iPlr);
if(pPlr)
C4PlayerControl *pTargetCtrl = NULL;
if (iPlr == -1)
{
// neutral control packet: Execute in global control
}
else
{
// player-based control: Execute on control owned by player
C4Player *pPlr=::Players.Get(iPlr);
if (pPlr)
{
if (!Inside<int>(iCom, COM_ReleaseFirst, COM_ReleaseLast))
pPlr->CountControl(C4Player::PCID_DirectCom, iCom*10000+iData);
pPlr->InCom(iCom, iData);
pTargetCtrl = &(pPlr->Control);
}
}
if (pTargetCtrl) pTargetCtrl->ExecuteControlPacket(this);
}
void C4ControlPlayerControl::ControlItem::CompileFunc(StdCompiler *pComp)
{
pComp->Value(iControl);
pComp->Seperator();
pComp->Value(iTriggerMode);
}
void C4ControlPlayerControl::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iPlr), "Player", -1));
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iCom), "Com", 0));
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iData), "Data", 0));
C4ControlPacket::CompileFunc(pComp);
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iPlr), "Player", -1));
pComp->Value(mkNamingAdapt(fRelease, "Release", false));
pComp->Value(mkNamingAdapt(ExtraData, "ExtraData", C4KeyEventData()));
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(ControlItems), "Controls", ControlItemVec()));
C4ControlPacket::CompileFunc(pComp);
}
// *** C4ControlPlayerCommand
C4ControlPlayerCommand::C4ControlPlayerCommand(int32_t iPlr, int32_t iCmd, int32_t iX, int32_t iY,

View File

@ -27,6 +27,7 @@
#include "C4PacketBase.h"
#include "C4PlayerInfo.h"
#include "C4Client.h"
#include "C4KeyboardInput.h"
#include "C4ObjectList.h"
// *** control base classes
@ -167,14 +168,34 @@ public:
class C4ControlPlayerControl : public C4ControlPacket // sync
{
public:
C4ControlPlayerControl()
: iPlr(-1), iCom(-1), iData(-1) { }
C4ControlPlayerControl(int32_t iPlr, int32_t iCom, int32_t iData)
: iPlr(iPlr), iCom(iCom), iData(iData) { }
C4ControlPlayerControl() : iPlr(-1), fRelease(false) {}
C4ControlPlayerControl(int32_t iPlr, bool fRelease, const C4KeyEventData &rExtraData)
: iPlr(iPlr), fRelease(fRelease), ExtraData(rExtraData) { }
C4ControlPlayerControl(int32_t iPlr, int32_t iControl, int32_t iExtraData) // old-style menu com emulation
: iPlr(iPlr), fRelease(false), ExtraData(iExtraData,0,0) { AddControl(iControl,0); }
struct ControlItem
{
int32_t iControl;
int32_t iTriggerMode;
ControlItem() : iControl(-1), iTriggerMode(0) {}
ControlItem(int32_t iControl, int32_t iTriggerMode) : iControl(iControl), iTriggerMode(iTriggerMode) {}
void CompileFunc(StdCompiler *pComp);
bool operator ==(const struct ControlItem &cmp) const { return iControl==cmp.iControl && iTriggerMode == cmp.iTriggerMode; }
};
typedef std::vector<ControlItem> ControlItemVec;
protected:
int32_t iPlr, iCom, iData;
int32_t iPlr;
bool fRelease;
C4KeyEventData ExtraData;
ControlItemVec ControlItems;
public:
DECLARE_C4CONTROL_VIRTUALS
void AddControl(int32_t iControl, int32_t iTriggerMode)
{ ControlItems.push_back(ControlItem(iControl, iTriggerMode)); }
const ControlItemVec &GetControlItems() const { return ControlItems; }
bool IsReleaseControl() const { return fRelease; }
const C4KeyEventData &GetExtraData() const { return ExtraData; }
};
class C4ControlPlayerCommand : public C4ControlPacket // sync

View File

@ -0,0 +1,876 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
// Input to player control mapping
#include <C4Include.h>
#include <C4PlayerControl.h>
#ifndef BIG_C4INCLUDE
#include <C4LangStringTable.h>
#include <C4Player.h>
#include <C4PlayerList.h>
#include <C4Control.h>
#include <C4Game.h>
#include <C4Log.h>
#endif
/* C4PlayerControlDef */
void C4PlayerControlDef::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("ControlDef")) { pComp->NameEnd(); pComp->excNotFound("ControlDef"); }
pComp->Value(mkNamingAdapt(mkParAdapt(sIdentifier, StdCompiler::RCT_Idtf), "Identifier", "None"));
pComp->Value(mkNamingAdapt(mkParAdapt(sGUIName, StdCompiler::RCT_All), "GUIName", "undefined"));
pComp->Value(mkNamingAdapt(mkParAdapt(sGUIDesc, StdCompiler::RCT_All), "GUIDesc", ""));
pComp->Value(mkNamingAdapt(fGlobal, "Global", false));
pComp->Value(mkNamingAdapt(fIsHoldKey, "Hold", false));
pComp->Value(mkNamingAdapt(iRepeatDelay, "RepeatDelay", 0));
pComp->Value(mkNamingAdapt(iInitialRepeatDelay, "InitialRepeatDelay", 0));
pComp->Value(mkNamingAdapt(fDefaultDisabled, "DefaultDisabled", false));
pComp->Value(mkNamingAdapt(mkC4IDAdapt(idControlExtraData), "ExtraData", C4ID_None));
const StdEnumEntry<Actions> ActionNames[] = {
{ "None", CDA_None },
{ "Script", CDA_Script },
{ "Menu", CDA_Menu },
{ "MenuOK", CDA_MenuOK },
{ "MenuCancel", CDA_MenuCancel },
{ "MenuLeft", CDA_MenuLeft },
{ "MenuUp", CDA_MenuUp },
{ "MenuRight", CDA_MenuRight },
{ "MenuDown", CDA_MenuDown },
{ NULL, CDA_None } };
pComp->Value(mkNamingAdapt(mkEnumAdapt<Actions, int32_t>(eAction, ActionNames), "Action", CDA_Script));
pComp->NameEnd();
}
bool C4PlayerControlDef::operator ==(const C4PlayerControlDef &cmp) const
{
return sIdentifier == cmp.sIdentifier
&& sGUIName == cmp.sGUIName
&& sGUIDesc == cmp.sGUIDesc
&& fGlobal == cmp.fGlobal
&& fIsHoldKey == cmp.fIsHoldKey
&& iRepeatDelay == cmp.iRepeatDelay
&& iInitialRepeatDelay == cmp.iInitialRepeatDelay
&& fDefaultDisabled == cmp.fDefaultDisabled
&& idControlExtraData == cmp.idControlExtraData
&& eAction == cmp.eAction;
}
/* C4PlayerControlDefs */
void C4PlayerControlDefs::UpdateInternalCons()
{
InternalCons.CON_MenuSelect = GetControlIndexByIdentifier("MenuSelect");
InternalCons.CON_MenuEnter = GetControlIndexByIdentifier("MenuEnter");
InternalCons.CON_MenuEnterAll = GetControlIndexByIdentifier("MenuEnterAll");
InternalCons.CON_MenuClose = GetControlIndexByIdentifier("MenuClose");
}
void C4PlayerControlDefs::Clear()
{
Defs.clear();
UpdateInternalCons();
}
void C4PlayerControlDefs::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Defs, StdCompiler::SEP_NONE), "ControlDefs", DefVecImpl()));
if (pComp->isCompiler()) UpdateInternalCons();
}
void C4PlayerControlDefs::MergeFrom(const C4PlayerControlDefs &Src)
{
// copy all defs from source file; overwrite defs of same name if found
for (DefVecImpl::const_iterator i = Src.Defs.begin(); i != Src.Defs.end(); ++i)
{
const C4PlayerControlDef &SrcDef = *i;
// overwrite if def of same name existed
int32_t iPrevIdx = GetControlIndexByIdentifier(SrcDef.GetIdentifier());
if (iPrevIdx != CON_None)
{
Defs[iPrevIdx] = SrcDef;
}
else
{
// new def: Append a copy
Defs.push_back(SrcDef);
}
}
UpdateInternalCons();
}
const C4PlayerControlDef *C4PlayerControlDefs::GetControlByIndex(int32_t idx) const
{
// safe index
if (idx<0 || idx>=int32_t(Defs.size())) return NULL;
return &(Defs[idx]);
}
int32_t C4PlayerControlDefs::GetControlIndexByIdentifier(const char *szIdentifier) const
{
for (DefVecImpl::const_iterator i = Defs.begin(); i != Defs.end(); ++i)
if (SEqual((*i).GetIdentifier(), szIdentifier))
return i-Defs.begin();
return CON_None;
}
void C4PlayerControlDefs::FinalInit()
{
// Assume all defs have been loaded
// Register scritp constants
for (DefVecImpl::const_iterator i = Defs.begin(); i != Defs.end(); ++i)
{
const char *szIdtf = (*i).GetIdentifier();
if (szIdtf && *szIdtf && !SEqual(szIdtf, "None"))
{
::ScriptEngine.RegisterGlobalConstant(FormatString("CON_%s", szIdtf).getData(), C4VInt(i-Defs.begin()));
}
}
}
/* C4PlayerControlAssignment */
void C4PlayerControlAssignment::KeyComboItem::CompileFunc(StdCompiler *pComp)
{
// if key is compiled, also store as a string into KeyName for later resolving
if (pComp->isCompiler())
{
sKeyName.Clear();
pComp->Value(mkParAdapt(Key, &sKeyName));
if (!sKeyName)
{
// key was not assigned during compilation - this means it's a regular key (or undefined)
// store this as the name
sKeyName.Copy(Key.ToString(false, false));
}
}
else
{
// decompiler: Just write the stored key name; regardless of whether it's a key, undefined or a reference
pComp->Value(mkParAdapt(sKeyName, StdCompiler::RCT_Idtf));
}
}
void C4PlayerControlAssignment::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("Assignment")) { pComp->NameEnd(); pComp->excNotFound("Assignment"); }
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(KeyCombo), "Key", KeyComboVec()));
pComp->Value(mkNamingAdapt(fComboIsSequence, "ComboIsSequence", false));
pComp->Value(mkNamingAdapt(mkParAdapt(sControlName, StdCompiler::RCT_Idtf), "Control", "None"));
pComp->Value(mkNamingAdapt(iPriority, "Priority", 0));
const StdBitfieldEntry<int32_t> TriggerModeNames[] = {
{ "Default", CTM_Default },
{ "Hold", CTM_Hold },
{ "Release", CTM_Release },
{ "AlwaysUnhandled", CTM_AlwaysUnhandled },
{ NULL, 0 } };
pComp->Value(mkNamingAdapt(mkBitfieldAdapt< int32_t>(iTriggerMode, TriggerModeNames), "TriggerMode", CTM_Default));
pComp->NameEnd();
// newly loaded structures are not resolved
if (pComp->isCompiler()) fRefsResolved = false;
}
bool C4PlayerControlAssignment::ResolveRefs(C4PlayerControlAssignmentSet *pParentSet, C4PlayerControlDefs *pControlDefs)
{
// avoid circular chains
static C4PlayerControlAssignment *pCircularDetect = NULL;
if (!pCircularDetect) pCircularDetect = this; else if (pCircularDetect == this)
{
LogFatal(FormatString("Circular reference chain detected in player control assignments of set %s in assignment for key %s!", pParentSet->GetName(), GetControlName()).getData());
return false;
}
// resolve control name
iControl = pControlDefs->GetControlIndexByIdentifier(sControlName.getData());
// resolve keys
KeyComboVec NewCombo;
for (KeyComboVec::iterator i = KeyCombo.begin(); i != KeyCombo.end(); ++i)
{
KeyComboItem &rKeyComboItem = *i;
if (rKeyComboItem.Key == KEY_Default && rKeyComboItem.sKeyName.getLength())
{
// this is a key reference
// it may be preceded by CON_ to avoid ambigous keus
const char *szKeyName = rKeyComboItem.sKeyName.getData();
if (SEqual2(szKeyName, "CON_")) szKeyName +=4;
// - find it
C4PlayerControlAssignment *pRefAssignment = pParentSet->GetAssignmentByControlName(szKeyName);
if (pRefAssignment)
{
// resolve itself if necessary
if (!pRefAssignment->IsRefsResolved()) if (!pRefAssignment->ResolveRefs(pParentSet, pControlDefs)) return false;
// insert all keys of that combo into own combo
NewCombo.insert(NewCombo.end(), pRefAssignment->KeyCombo.begin(), pRefAssignment->KeyCombo.end());
}
else
{
// undefined reference? Not fatal, but inform user
LogF("WARNING: Control %s of set %s contains reference to unassigned control %s.", GetControlName(), pParentSet->GetName(), rKeyComboItem.sKeyName.getData());
NewCombo.clear();
}
}
else
{
NewCombo.push_back(rKeyComboItem);
}
}
KeyCombo = NewCombo;
// the trigger key is always last of the chain
if (KeyCombo.size()) TriggerKey = KeyCombo.back().Key; else TriggerKey = C4KeyCodeEx();
// done
fRefsResolved = true;
if (pCircularDetect == this) pCircularDetect = NULL;
return true;
}
bool C4PlayerControlAssignment::IsComboMatched(const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const
{
assert(HasCombo());
// check if combo is currently fulfilled (assuming TriggerKey is already matched)
if (fComboIsSequence)
{
DWORD tKeyLast = timeGetTime();
// combo is a sequence: The last keys of RecentKeys must match the sequence
// the last ComboKey is the TriggerKey, which is omitted because it has already been matched and is not to be found in RecentKeys yet
C4PlayerControlRecentKeyList::const_reverse_iterator ri = RecentKeys.rbegin();
for (KeyComboVec::const_reverse_iterator i = KeyCombo.rbegin()+1; i!=KeyCombo.rend(); ++i,++ri)
{
// no more keys pressed but combo didn't end? -> no combo match
if (ri == RecentKeys.rend()) return false;
const C4PlayerControlRecentKey &rk = *ri;
// user waited for too long?
DWORD tKeyRecent = rk.tTime;
if (tKeyLast - tKeyRecent > C4PlayerControl::MaxSequenceKeyDelay) return false;
// key doesn't match?
const KeyComboItem &k = *i;
if (!(rk.Key == k.Key)) return false;
// key OK
}
}
else
{
// combo requires keys to be down simultanuously: check that all keys of the combo are in the down-list
for (KeyComboVec::const_iterator i = KeyCombo.begin(); i!=KeyCombo.end(); ++i)
{
const KeyComboItem &k = *i;
bool fFound = false;
for (C4PlayerControlRecentKeyList::const_iterator di = DownKeys.begin(); di!=DownKeys.end(); ++di)
{
const C4PlayerControlRecentKey &dk = *di;
if (dk.Key == k.Key) { fFound = true; break; }
}
if (!fFound) return false;
}
}
// combo OK!
return true;
}
bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp) const
{
// doesn't compare resolved TriggerKey/iControl
return KeyCombo == cmp.KeyCombo
&& sControlName == cmp.sControlName
&& iTriggerMode == cmp.iTriggerMode
&& iPriority == cmp.iPriority;
}
/* C4PlayerControlAssignmentSet */
void C4PlayerControlAssignmentSet::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("ControlSet")) { pComp->NameEnd(); pComp->excNotFound("ControlSet"); }
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();
}
void C4PlayerControlAssignmentSet::MergeFrom(const C4PlayerControlAssignmentSet &Src, bool fLowPrio)
{
// take over all assignments defined in Src
for (C4PlayerControlAssignmentVec::const_iterator i = Src.Assignments.begin(); i != Src.Assignments.end(); ++i)
{
const C4PlayerControlAssignment &SrcAssignment = *i;
// overwrite if def of same name existed if it's not low priority anyway?
// not so easy. Keys may be assigned to multiple controls and we may need to overwrite one or more of them...
C4PlayerControlAssignment *pPrevAssignment = GetAssignmentByControlName(SrcAssignment.GetControlName());
if (pPrevAssignment)
{
if (!fLowPrio) *pPrevAssignment = SrcAssignment;
}
else
{
// new def: Append a copy
Assignments.push_back(SrcAssignment);
}
}
}
bool C4PlayerControlAssignmentSet::ResolveRefs(C4PlayerControlDefs *pDefs)
{
// resolve in order; ignore already resolved because they might have been resolved by cross reference
for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
if (!(*i).IsRefsResolved())
if (!(*i).ResolveRefs(this, pDefs))
return false;
// now sort assignments by priority
std::sort(Assignments.begin(), Assignments.end());
return true;
}
C4PlayerControlAssignment *C4PlayerControlAssignmentSet::GetAssignmentByControlName(const char *szControlName)
{
for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
if (SEqual((*i).GetControlName(), szControlName))
// We don't like release keys... (2do)
if (!((*i).GetTriggerMode() & C4PlayerControlAssignment::CTM_Release))
return &*i;
return NULL;
}
bool C4PlayerControlAssignmentSet::operator ==(const C4PlayerControlAssignmentSet &cmp) const
{
return Assignments == cmp.Assignments
&& sName == cmp.sName;
}
void C4PlayerControlAssignmentSet::GetAssignmentsByKey(const C4PlayerControlDefs &rDefs, const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentPVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const
{
assert(pOutVec);
// primary match by TriggerKey
for (C4PlayerControlAssignmentVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i)
{
const C4PlayerControlAssignment &rAssignment = *i;
if (!(rAssignment.GetTriggerKey() == key)) continue;
// check linked control def
const C4PlayerControlDef *pCtrl = rDefs.GetControlByIndex(rAssignment.GetControl());
if (!pCtrl) continue;
// only want hold keys?
if (fHoldKeysOnly)
{
// a hold/release-trigger key is not a real hold key, even if the underlying control is
if (!pCtrl->IsHoldKey() || (rAssignment.GetTriggerMode() & (C4PlayerControlAssignment::CTM_Hold | C4PlayerControlAssignment::CTM_Release))) continue;
}
else if (rAssignment.HasCombo())
{
// hold-only events match the trigger key only (i.e., Release-events are generated as soon as the trigger key goes up)
// other events must match either the sequence or the down-key-combination
if (!rAssignment.IsComboMatched(DownKeys, RecentKeys)) continue;
}
// we got match! Store it
pOutVec->push_back(&rAssignment);
}
}
void C4PlayerControlAssignmentSet::GetTriggerKeys(const C4PlayerControlDefs &rDefs, C4KeyCodeExVec *pRegularKeys, C4KeyCodeExVec *pHoldKeys) const
{
// put all trigger keys of keyset into output vectors
// first all hold keys
for (C4PlayerControlAssignmentVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i)
{
const C4PlayerControlAssignment &rAssignment = *i;
const C4PlayerControlDef *pDef = rDefs.GetControlByIndex(rAssignment.GetControl());
if (pDef && pDef->IsHoldKey())
{
const C4KeyCodeEx &rKey = rAssignment.GetTriggerKey();
if (std::find(pHoldKeys->begin(), pHoldKeys->end(), rKey) == pHoldKeys->end()) pHoldKeys->push_back(rKey);
}
}
// then all regular keys that aren't in the hold keys list yet
for (C4PlayerControlAssignmentVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i)
{
const C4PlayerControlAssignment &rAssignment = *i;
const C4PlayerControlDef *pDef = rDefs.GetControlByIndex(rAssignment.GetControl());
if (pDef && !pDef->IsHoldKey())
{
const C4KeyCodeEx &rKey = rAssignment.GetTriggerKey();
if (std::find(pHoldKeys->begin(), pHoldKeys->end(), rKey) == pHoldKeys->end())
if (std::find(pRegularKeys->begin(), pRegularKeys->end(), rKey) == pRegularKeys->end())
pRegularKeys->push_back(rKey);
}
}
}
/* C4PlayerControlAssignmentSets */
void C4PlayerControlAssignmentSets::Clear()
{
Sets.clear();
}
void C4PlayerControlAssignmentSets::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Sets, StdCompiler::SEP_NONE), "ControlSets", AssignmentSetList()));
}
void C4PlayerControlAssignmentSets::MergeFrom(const C4PlayerControlAssignmentSets &Src, bool fLowPrio)
{
// take over all assignments in known sets and new sets defined in Src
for (AssignmentSetList::const_iterator i = Src.Sets.begin(); i != Src.Sets.end(); ++i)
{
const C4PlayerControlAssignmentSet &SrcSet = *i;
// overwrite if def of same name existed if it's not low priority anyway
bool fIsWildcardSet = SrcSet.IsWildcardName();
if (!fIsWildcardSet)
{
C4PlayerControlAssignmentSet *pPrevSet = GetSetByName(SrcSet.GetName());
if (pPrevSet)
{
pPrevSet->MergeFrom(SrcSet, fLowPrio);
}
else
{
// new def: Append a copy
Sets.push_back(SrcSet);
}
}
else
{
// 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);
}
}
}
}
}
bool C4PlayerControlAssignmentSets::ResolveRefs(C4PlayerControlDefs *pDefs)
{
for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i)
if (!(*i).ResolveRefs(pDefs)) return false;
return true;
}
C4PlayerControlAssignmentSet *C4PlayerControlAssignmentSets::GetSetByName(const char *szName)
{
for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i)
if (SEqual((*i).GetName(), szName))
return &*i;
return NULL;
}
/* C4PlayerControlFile */
void C4PlayerControlFile::CompileFunc(StdCompiler *pComp)
{
pComp->Value(ControlDefs);
pComp->Value(AssignmentSets);
}
bool C4PlayerControlFile::Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang)
{
// clear previous
Clear();
// load and prepare file contents
StdStrBuf Buf;
if (!hGroup.LoadEntryString(szFilename, Buf)) return false;
if (pLang) pLang->ReplaceStrings(Buf);
// parse it!
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Buf, szFilename)) return false;
return true;
}
bool C4PlayerControlFile::Save(C4Group &hGroup, const char *szFilename)
{
// decompile to buffer and save buffer to group
StdStrBuf Buf;
if (!DecompileToBuf_Log<StdCompilerINIWrite>(*this, &Buf, szFilename)) return false;
hGroup.Add(szFilename, Buf, false, true);
return true;
}
void C4PlayerControlFile::Clear()
{
ControlDefs.Clear();
AssignmentSets.Clear();
}
/* C4PlayerControl */
void C4PlayerControl::CSync::ControlDownState::CompileFunc(StdCompiler *pComp)
{
pComp->Value(DownState);
pComp->Seperator();
pComp->Value(iDownFrame);
pComp->Seperator();
pComp->Value(fDownByUser);
}
bool C4PlayerControl::CSync::ControlDownState::operator ==(const ControlDownState &cmp) const
{
return DownState == cmp.DownState && iDownFrame == cmp.iDownFrame && fDownByUser == cmp.fDownByUser;
}
const C4PlayerControl::CSync::ControlDownState *C4PlayerControl::CSync::GetControlDownState(int32_t iControl) const
{
// safe access
if (iControl < 0 || iControl >= int32_t(ControlDownStates.size())) return NULL;
return &ControlDownStates[iControl];
}
int32_t C4PlayerControl::CSync::GetControlDisabled(int32_t iControl) const
{
// safe access
if (iControl < 0 || iControl >= int32_t(ControlDisableStates.size())) return 0;
return ControlDisableStates[iControl];
}
void C4PlayerControl::CSync::SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser)
{
// update state
if (iControl < 0) return;
if (iControl >= int32_t(ControlDownStates.size())) ControlDownStates.resize(iControl+1);
ControlDownState &rState = ControlDownStates[iControl];
rState.DownState = rDownState;
rState.iDownFrame = iDownFrame;
rState.fDownByUser = fDownByUser;
}
void C4PlayerControl::CSync::SetControlDisabled(int32_t iControl, int32_t iVal)
{
// disable control
if (iControl < 0) return;
if (iControl >= int32_t(ControlDisableStates.size())) ControlDisableStates.resize(iControl+1);
ControlDisableStates[iControl] = iVal;
// if a control is disabled, its down-state is reset silently
const ControlDownState *pDownState = GetControlDownState(iControl);
if (pDownState && pDownState->IsDown())
{
C4KeyEventData KeyDownState = pDownState->DownState;
KeyDownState.iStrength = 0;
SetControlDownState(iControl, KeyDownState, 0, false);
}
}
void C4PlayerControl::CSync::Clear()
{
ControlDownStates.clear();
ControlDisableStates.clear();
}
void C4PlayerControl::CSync::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(ControlDownStates), "Down", DownStateVec()));
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(ControlDisableStates), "Disabled", DisableStateVec()));
}
bool C4PlayerControl::CSync::operator ==(const CSync &cmp) const
{
return ControlDownStates == cmp.ControlDownStates
&& ControlDisableStates == cmp.ControlDisableStates;
}
void C4PlayerControl::CompileFunc(StdCompiler *pComp)
{
// compile sync values only
pComp->Value(mkNamingAdapt(Sync, "PlayerControl", CSync()));
}
bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4KeyEventData &rKeyExtraData)
{
// collect all matching keys
C4PlayerControlAssignmentPVec Matches;
assert(pControlSet); // shouldn't get this callback for players without control set
pControlSet->GetAssignmentsByKey(ControlDefs, key, fUp, &Matches, DownKeys, RecentKeys);
// process async controls
C4ControlPlayerControl *pControlPacket = NULL;
for (C4PlayerControlAssignmentPVec::const_iterator i = Matches.begin(); i != Matches.end(); ++i)
{
const C4PlayerControlAssignment *pAssignment = *i;
assert(pAssignment);
int32_t iControlIndex = pAssignment->GetControl();
const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControlIndex);
if (pControlDef && pControlDef->IsValid() && (!fUp || pControlDef->IsHoldKey()))
{
if (pControlDef->IsAsync() && !pControlPacket)
{
if (ExecuteControl(iControlIndex, fUp, rKeyExtraData, pAssignment->GetTriggerMode(), key.IsRepeated()))
return true;
}
else
{
// sync control
// ignore key repeats, because we do our own key repeat for sync controls
if (key.IsRepeated()) return false;
// sync control has higher priority - no more async execution then
// 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());
}
}
}
// push sync control to input
if (pControlPacket)
{
Game.Input.Add(CID_PlrControl2, pControlPacket);
// assume processed (although we can't really know that yet)
return true;
}
return false;
}
bool C4PlayerControl::ProcessKeyDown(const C4KeyCodeEx &key)
{
// add key to local "down" list if it's not already in there
C4PlayerControlRecentKey RKey(key,timeGetTime());
if (std::find(DownKeys.begin(), DownKeys.end(), C4PlayerControlRecentKey(key,0)) == DownKeys.end()) DownKeys.push_back(RKey);
// process!
bool fResult = ProcessKeyEvent(key, false, Game.KeyboardInput.GetLastKeyExtraData());
// add to recent list unless repeated
if (!key.IsRepeated()) RecentKeys.push_back(RKey);
return fResult;
}
bool C4PlayerControl::ProcessKeyUp(const C4KeyCodeEx &key)
{
// remove key from "down" list
C4PlayerControlRecentKeyList::iterator i = find(DownKeys.begin(), DownKeys.end(), C4PlayerControlRecentKey(key,0));
if (i != DownKeys.end()) DownKeys.erase(i);
// process!
return ProcessKeyEvent(key, true, Game.KeyboardInput.GetLastKeyExtraData());
}
void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl)
{
// callback from control queue. Execute controls in packet until one of them gets processed
// assume async packets always as not processed to ensure sync safety (usually, sync commands should better not ovberride async commands anyway)
for (C4ControlPlayerControl::ControlItemVec::const_iterator i = pCtrl->GetControlItems().begin(); i != pCtrl->GetControlItems().end(); ++i)
{
const C4ControlPlayerControl::ControlItem &rItem = *i;
const C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(rItem.iControl);
if (pCtrlDef)
{
if (ExecuteControl(rItem.iControl, pCtrl->IsReleaseControl(), pCtrl->GetExtraData(), rItem.iTriggerMode, false))
if (pCtrlDef->IsSync())
break;
}
}
}
bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated)
{
// execute single control. return if handled
const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControl);
if (!pControlDef || Sync.IsControlDisabled(iControl)) return false;
C4PlayerControlDef::Actions eAction = pControlDef->GetAction();
C4KeyEventData KeyExtraData(rKeyExtraData);
const CSync::ControlDownState *pCtrlDownState = Sync.GetControlDownState(iControl);
bool fWasDown = pCtrlDownState ? pCtrlDownState->IsDown() : false;
// global controls only in global context
if (IsGlobal() != pControlDef->IsGlobal()) return false;
// hold-actions only work on script controls with the hold flag
if (iTriggerMode & (C4PlayerControlAssignment::CTM_Hold | C4PlayerControlAssignment::CTM_Release))
{
if (eAction != C4PlayerControlDef::CDA_Script) return false;
if (!pControlDef->IsHoldKey()) return false;
if (fUp) return false; // hold triggers have no "up"-event
// perform hold/release
if (fWasDown)
{
// control is currently down: release?
if (iTriggerMode & C4PlayerControlAssignment::CTM_Release)
{
KeyExtraData.iStrength = 0;
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
// now process as a regular "Up" event
fUp = true;
fRepeated = false;
}
else //if (iTriggerMode & C4PlayerControlAssignment::CTM_Hold) - must be true
{
// control is down but trigger key is pressed again: Refresh down state
// (this will restart the KeyRepeat time)
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
// now process as a regular, repeated "down" event
fRepeated = true;
}
}
else
{
// control is currently up. Put into hold-down-state if this is a hold key
if (iTriggerMode & C4PlayerControlAssignment::CTM_Hold)
{
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
// now process as a regular "down" event
fRepeated = false;
}
else
{
//. Ignore if it's only a release key
return false;
}
}
}
else if (fUp)
{
// regular ControlUp: Only valid if that control was down
if (!fWasDown) return false;
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, true);
}
else if (pControlDef->IsHoldKey())
{
// regular ControlDown on Hold Key: Set in down list
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, true);
fRepeated = fWasDown;
}
// perform action for this control
bool fHandled = ExecuteControlAction(iControl, eAction, pControlDef->GetExtraData(), fUp, KeyExtraData, fRepeated);
// return if handled, unless control is defined as always unhandled
return fHandled && !(iTriggerMode & C4PlayerControlAssignment::CTM_AlwaysUnhandled);
}
bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated)
{
// get affected player
C4Player *pPlr = NULL;
if (iPlr > -1)
{
pPlr = ::Players.Get(iPlr);
if (!pPlr) return false;
}
// exec action (on player)
switch (eAction)
{
// scripted player control
case C4PlayerControlDef::CDA_Script:
return ExecuteControlScript(iControl, idControlExtraData, fUp, rKeyExtraData, fRepeated);
// menu controls
case C4PlayerControlDef::CDA_Menu: if (!pPlr || fUp) return false; if (pPlr->Menu.IsActive()) pPlr->Menu.Close(false); else pPlr->ActivateMenuMain(); return true; // toggle
case C4PlayerControlDef::CDA_MenuOK: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuEnter,0); return true; // ok on item
case C4PlayerControlDef::CDA_MenuCancel: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuClose,0); return true; // close menu
case C4PlayerControlDef::CDA_MenuLeft: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuLeft ,0); return true; // navigate
case C4PlayerControlDef::CDA_MenuUp: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuUp ,0); return true; // navigate
case C4PlayerControlDef::CDA_MenuRight: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuRight,0); return true; // navigate
case C4PlayerControlDef::CDA_MenuDown: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuDown ,0); return true; // navigate
//unknown action
default: return false;
}
}
bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated)
{
C4Player *pPlr = ::Players.Get(iPlr);
if (pPlr)
{
// Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!)
if (pPlr->Eliminated) return false;
// control count for statistics
pPlr->CountControl(C4Player::PCID_DirectCom, iControl*2+fUp);
}
else if (iPlr > -1)
{
// player lost?
return false;
}
// control down
C4AulFunc *pFunc = ::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);
}
void C4PlayerControl::Execute()
{
// sync execution: Do keyrepeat
for (int32_t i=0; i<ControlDefs.GetCount(); ++i)
{
const CSync::ControlDownState *pControlDownState = Sync.GetControlDownState(i);
if (pControlDownState && pControlDownState->IsDown())
{
const C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(i);
assert(pCtrlDef);
int32_t iCtrlRepeatDelay = pCtrlDef->GetRepeatDelay();
if (iCtrlRepeatDelay)
{
int32_t iFrameDiff = Game.FrameCounter - pControlDownState->iDownFrame;
int32_t iCtrlInitialRepeatDelay = pCtrlDef->GetInitialRepeatDelay();
if (iFrameDiff && iFrameDiff >= iCtrlInitialRepeatDelay)
{
if (!((iFrameDiff-iCtrlInitialRepeatDelay) % iCtrlRepeatDelay))
{
// it's RepeatTime for this key!
ExecuteControlAction(i, pCtrlDef->GetAction(), pCtrlDef->GetExtraData(), false, pControlDownState->DownState, true);
}
}
}
}
}
// cleanup old recent keys
C4PlayerControlRecentKeyList::iterator irk;
DWORD tNow = timeGetTime();
for (irk = RecentKeys.begin(); irk != RecentKeys.end(); ++irk)
{
C4PlayerControlRecentKey &rk = *irk;
if (rk.tTime + MaxRecentKeyLookback > tNow) break;
}
if (irk != RecentKeys.begin()) RecentKeys.erase(RecentKeys.begin(), irk);
}
C4PlayerControl::C4PlayerControl() : ControlDefs(Game.PlayerControlDefs), iPlr(-1), pControlSet(NULL)
{
}
void C4PlayerControl::Clear()
{
iPlr = NO_OWNER;
pControlSet = NULL;
for (KeyBindingList::iterator i = KeyBindings.begin(); i != KeyBindings.end(); ++i) delete *i;
KeyBindings.clear();
RecentKeys.clear();
DownKeys.clear();
Sync.Clear();
}
void C4PlayerControl::RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset)
{
// clear any previous settings
Clear();
// setup
pControlSet = pKeyset;
this->iPlr = iPlr;
// register all keys into Game.KeyboardInput creating KeyBindings
if (pControlSet)
{
C4KeyCodeExVec RegularKeys, HoldKeys;
pControlSet->GetTriggerKeys(ControlDefs, &RegularKeys, &HoldKeys);
int32_t idx=0;
for (C4KeyCodeExVec::const_iterator i = RegularKeys.begin(); i != RegularKeys.end(); ++i) AddKeyBinding(*i, false, idx++);
for (C4KeyCodeExVec::const_iterator i = HoldKeys.begin(); i != HoldKeys.end(); ++i) AddKeyBinding(*i, true, idx++);
}
}
void C4PlayerControl::AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx)
{
KeyBindings.push_back(new C4KeyBinding(
key, FormatString("PlrKey%02d", idx).getData(), KEYSCOPE_Control,
new C4KeyCBPassKey<C4PlayerControl>(*this, &C4PlayerControl::ProcessKeyDown, fHoldKey ? &C4PlayerControl::ProcessKeyUp : NULL),
C4CustomKey::PRIO_PlrControl));
}

View File

@ -0,0 +1,338 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
// Input to player control mapping
#ifndef INC_C4PlayerControl
#define INC_C4PlayerControl
#ifndef BIG_C4INCLUDE
#include <C4KeyboardInput.h>
#include <C4LangStringTable.h>
#endif
// one control definition, e.g. "Left", "Throw", etc.
class C4PlayerControlDef
{
private:
StdCopyStrBuf sIdentifier; // name as seen in script and config
StdCopyStrBuf sGUIName; // name as displayed to player
StdCopyStrBuf sGUIDesc; // key description displayed to player in config dialog
bool fGlobal; // if true, control can be bound to the global player only
bool fIsHoldKey; // if true, the control can be in down and up state
int32_t iRepeatDelay; // if >0, the key will generate successive events when held down
int32_t iInitialRepeatDelay; // delay after which KeyRepeat will be enabled
bool fDefaultDisabled; // if true, the control is disabled by default and needs to be enabled by script
C4ID idControlExtraData; // extra data to be passed to script function
public:
enum Actions //action to be performed when control is triggered
{
CDA_None=0, // do nothing
CDA_Script, // default: Script callback
CDA_Menu, // open player menu (async)
CDA_MenuOK, CDA_MenuCancel, CDA_MenuLeft, CDA_MenuUp, CDA_MenuRight, CDA_MenuDown, // player menu controls (async)
};
private:
Actions eAction;
public:
C4PlayerControlDef() : fIsHoldKey(false), fDefaultDisabled(false), eAction(CDA_Script), fGlobal(false), idControlExtraData(C4ID_None) {}
~C4PlayerControlDef() {};
void CompileFunc(StdCompiler *pComp);
const char *GetIdentifier() const { return sIdentifier.getData(); }
const char *GetGUIName() const { return sGUIName.getData(); }
const char *GetGUIDesc() const { return sGUIDesc.getData(); }
Actions GetAction() const { return eAction; }
bool IsHoldKey() const { return fIsHoldKey; }
C4ID GetExtraData() const { return idControlExtraData; }
bool IsGlobal() const { return fGlobal; }
int32_t GetRepeatDelay() const { return iRepeatDelay; }
int32_t GetInitialRepeatDelay() const { return iInitialRepeatDelay; }
//C4PlayerControlDef &operator =(const C4PlayerControlDef &src);
bool operator ==(const C4PlayerControlDef &cmp) const;
bool Execute(bool fUp, const C4KeyEventData &rKeyExtraData); // key was triggered - execute and return if handled
bool IsAsync() const { return eAction != CDA_None && eAction != CDA_Script; } // true if to be executed directly when triggered
bool IsSync() const { return eAction == CDA_Script; } // true if to be executed via control queue
bool IsValid() const { return eAction != CDA_None; }
};
// CON_* constants are indices into the C4PlayerControlDefs list
enum { CON_None = -1 }; // No/undefined control
// list of all known player control definitions
class C4PlayerControlDefs
{
private:
typedef std::vector<C4PlayerControlDef> DefVecImpl;
DefVecImpl Defs;
public:
struct CInternalCons
{
int32_t CON_MenuSelect, CON_MenuEnterAll, CON_MenuEnter, CON_MenuClose;
CInternalCons() : CON_MenuSelect(CON_None), CON_MenuEnterAll(CON_None), CON_MenuEnter(CON_None), CON_MenuClose(CON_None) {}
} InternalCons;
void UpdateInternalCons();
public:
C4PlayerControlDefs() {}
~C4PlayerControlDefs() {}
void Clear();
void CompileFunc(StdCompiler *pComp);
void MergeFrom(const C4PlayerControlDefs &Src); // copy all defs from source file; overwrite defs of same name if found
void FinalInit(); // after all defs have been loaded: register script constants
const C4PlayerControlDef *GetControlByIndex(int32_t idx) const;
int32_t GetControlIndexByIdentifier(const char *szIdentifier) const; // return CON_None for not found
int32_t GetCount() const { return Defs.size(); }
bool operator ==(const C4PlayerControlDefs &cmp) const { return Defs == cmp.Defs; }
};
struct C4PlayerControlRecentKey
{
C4KeyCodeEx Key;
DWORD tTime;
C4PlayerControlRecentKey(const C4KeyCodeEx &Key, DWORD tTime) : Key(Key), tTime(tTime) {}
bool operator ==(const C4PlayerControlRecentKey &cmp) { return Key==cmp.Key; } // comparison op for finding items in lists: Search for the key only
};
typedef std::list<C4PlayerControlRecentKey> C4PlayerControlRecentKeyList;
typedef std::vector<C4KeyCodeEx> C4KeyCodeExVec;
// a key/mouse/gamepad assignment to a PlayerControlDef
class C4PlayerControlAssignment
{
private:
// KeyCombo list:
// if size()>1, the control is triggered only if this combo is fulfilled
// used for simultanuous keypresses or sequences
struct KeyComboItem
{
C4KeyCodeEx Key;
StdCopyStrBuf sKeyName;
void CompileFunc(StdCompiler *pComp);
bool operator ==(const KeyComboItem &cmp) const { return sKeyName==cmp.sKeyName; }
};
typedef std::vector<KeyComboItem> KeyComboVec;
KeyComboVec KeyCombo;
bool fComboIsSequence; // if true, the keys must be pressed in sequence. Otherwise, they must be pressed simultanuously
// trigger key: key/mouse/gamepad event triggering this assignment. For combinations, the last key of the combo.
C4KeyCodeEx TriggerKey;
StdCopyStrBuf sControlName; // name of the control to be executed on this key
int32_t iControl; // the control to be executed on this key, i.e. the resolved sControlName
int32_t iPriority; // higher priority assignments get handled first
public:
// action to be performed on the control upon this key
enum TriggerModes
{
CTM_Default=0, // standard behaviour: The control will be triggered
CTM_Hold= 1<<0, // the control will be put into "down"-mode
CTM_Release= 1<<1, // the hold mode of the control will be released
CTM_AlwaysUnhandled= 1<<2, // the key will not block handling of other keys even if it got handled
};
private:
int32_t iTriggerMode;
bool fRefsResolved; // set to true after sControlName and sKeyNames have been resolved to runtime values
public:
C4PlayerControlAssignment() : TriggerKey(), iControl(CON_None), iTriggerMode(CTM_Default), iPriority(0), fRefsResolved(false) {}
~C4PlayerControlAssignment() {}
void CompileFunc(StdCompiler *pComp);
bool ResolveRefs(class C4PlayerControlAssignmentSet *pParentSet, C4PlayerControlDefs *pControlDefs); // resolve references between assignments
bool IsComboMatched(const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // check if combo is currently fulfilled (assuming TriggerKey is already matched)
bool operator ==(const C4PlayerControlAssignment &cmp) const; // doesn't compare resolved TriggerKey/iControl
bool operator <(const C4PlayerControlAssignment &cmp) const { return iPriority > cmp.iPriority; } // assignments are processed in DESCENDING priority!
const char *GetControlName() const { return sControlName.getData(); }
int32_t GetControl() const { return iControl; }
bool IsRefsResolved() const { return fRefsResolved; }
bool IsAlwaysUnhandled() const { return !!(iTriggerMode & CTM_AlwaysUnhandled); }
int32_t GetTriggerMode() const { return iTriggerMode; }
const C4KeyCodeEx &GetTriggerKey() const { return TriggerKey; }
bool HasCombo() const { return KeyCombo.size()>1; }
};
typedef std::vector<C4PlayerControlAssignment> C4PlayerControlAssignmentVec;
typedef std::vector<const C4PlayerControlAssignment *> C4PlayerControlAssignmentPVec;
// a set of key/mouse/gamepad assignments to all controls
class C4PlayerControlAssignmentSet
{
private:
StdCopyStrBuf sName;
C4PlayerControlAssignmentVec Assignments;
public:
C4PlayerControlAssignmentSet() {}
~C4PlayerControlAssignmentSet() {}
void CompileFunc(StdCompiler *pComp);
bool ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments
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
void GetTriggerKeys(const C4PlayerControlDefs &rDefs, C4KeyCodeExVec *pRegularKeys, C4KeyCodeExVec *pHoldKeys) const; // put all trigger keys of keyset into output vectors
bool operator ==(const C4PlayerControlAssignmentSet &cmp) const;
};
// list of C4PlayerControlAssignmentSet
class C4PlayerControlAssignmentSets
{
private:
typedef std::list<C4PlayerControlAssignmentSet> AssignmentSetList;
AssignmentSetList Sets;
public:
C4PlayerControlAssignmentSets() {}
~C4PlayerControlAssignmentSets() {}
void Clear();
void CompileFunc(StdCompiler *pComp);
bool ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments
void MergeFrom(const C4PlayerControlAssignmentSets &Src, bool fLowPrio); // take over all assignments in known sets and new sets defined in Src
C4PlayerControlAssignmentSet *GetSetByName(const char *szName);
};
// contents of one PlayerControls.txt file
class C4PlayerControlFile
{
private:
C4PlayerControlDefs ControlDefs;
C4PlayerControlAssignmentSets AssignmentSets;
public:
void Clear();
void CompileFunc(StdCompiler *pComp);
bool Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang);
bool Save(C4Group &hGroup, const char *szFilename);
const C4PlayerControlDefs &GetControlDefs() const { return ControlDefs; }
const C4PlayerControlAssignmentSets &GetAssignmentSets() const { return AssignmentSets; }
};
// runtime information about a player's control
class C4PlayerControl
{
public:
enum { MaxRecentKeyLookback = 3000, MaxSequenceKeyDelay = 800 }; // milliseconds: Time to press key combos
private:
C4PlayerControlDefs &ControlDefs; // shortcut
// owner
int32_t iPlr;
// async values
C4PlayerControlAssignmentSet *pControlSet; // the control set used by this player - may be NULL if the player cannot be controlled!
typedef std::list<C4KeyBinding *> KeyBindingList;
KeyBindingList KeyBindings; // keys registered into Game.KeyboardInput
C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations
C4PlayerControlRecentKeyList DownKeys; // keys currently held down
public:
// sync values
struct CSync
{
struct ControlDownState
{
C4KeyEventData DownState; // control is down if DownState.iStrength>0
int32_t iDownFrame; // frame when control was pressed
bool fDownByUser; // if true, the key is actually pressed. Otherwise, it's triggered as down by another key
ControlDownState(const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser)
: DownState(rDownState), iDownFrame(iDownFrame), fDownByUser(fDownByUser) {}
bool IsDown() const { return DownState.iStrength>0; }
ControlDownState() : DownState(), iDownFrame(0), fDownByUser(false) {}
void CompileFunc(StdCompiler *pComp);
bool operator ==(const ControlDownState &cmp) const;
};
typedef std::vector<ControlDownState> DownStateVec;
DownStateVec ControlDownStates; // indexed by C4PlayerControlID: Down-state of a control. 0=up, 100=down; values inbetween e.g. for gamepad sticks
typedef std::vector<int32_t> DisableStateVec;
DisableStateVec ControlDisableStates; // indexed by C4PlayerControlID: Disable-states of controls. >0 is disabled.
const ControlDownState *GetControlDownState(int32_t iControl) const;
int32_t GetControlDisabled(int32_t iControl) const;
bool IsControlDisabled(int32_t iControl) const { return GetControlDisabled(iControl)>0; }
void SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser);
void SetControlDisabled(int32_t iControl, int32_t iVal);
void Clear();
void CompileFunc(StdCompiler *pComp);
bool operator ==(const CSync &cmp) const;
};
private:
CSync Sync;
// callbacks from Game.KeyboardInput
bool ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4KeyEventData &rKeyExtraData);
bool ProcessKeyDown(const C4KeyCodeEx &key);
bool ProcessKeyUp(const C4KeyCodeEx &key);
// execute single control. return if handled.
bool ExecuteControl(int32_t iControl, bool fUp, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated);
bool ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated);
bool ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated);
// init
void AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx);
public:
C4PlayerControl();
~C4PlayerControl() { Clear(); }
void Clear();
void CompileFunc(StdCompiler *pComp);
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);
// sync execution: Do keyrepeat, etc.
void Execute();
};
#endif // INC_C4PlayerControl

View File

@ -255,7 +255,7 @@ bool C4Record::Rec(C4PacketType eCtrlType, C4ControlPacket *pCtrl, int iFrame)
bool C4Record::Rec(int iFrame, const StdBuf &sBuf, C4RecordChunkType eType)
{
// filler chunks (this should never be necessary, though)
while(iFrame > iLastFrame + 0xff)
while(iFrame > int(iLastFrame + 0xff))
Rec(iLastFrame + 0xff, StdBuf(), RCT_Frame);
// get frame difference
uint8_t iFrameDiff = Max<uint8_t>(0, iFrame - iLastFrame);
@ -990,7 +990,7 @@ StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf & RawData)
case RCT_AulFunc: r.Ref(reinterpret_cast<const char*>(RawData.getData()), RawData.getSize()-1);
break;
default:
for (int i=0; i<RawData.getSize(); ++i)
for (unsigned int i=0; i<RawData.getSize(); ++i)
r.AppendFormat("%02x ", (uint32_t) ((uint8_t *)RawData.getData())[i]);
break;
}

View File

@ -2772,7 +2772,7 @@ bool C4Landscape::Mat2Pal()
for (rgb=0; rgb<3; rgb++)
Surface8->pPal->Colors[MatTex2PixCol(tex)*3+rgb]
= Surface8->pPal->Colors[(MatTex2PixCol(tex)+IFT)*3+rgb]
= dwPix >> ((2-rgb) * 8);
= uint8_t(dwPix >> ((2-rgb) * 8));
// alpha
Surface8->pPal->Alpha[MatTex2PixCol(tex)] = 0;
Surface8->pPal->Alpha[MatTex2PixCol(tex)+IFT] = 0;

View File

@ -353,6 +353,7 @@ void C4GameObjects::PutSolidMasks()
void C4GameObjects::DeleteObjects(bool fDeleteInactive)
{
Sectors.ClearObjects();
C4ObjectList::DeleteObjects();
BackObjects.Clear();
ForeObjects.Clear();

View File

@ -3174,499 +3174,6 @@ void C4Object::Clear()
if (pGfxOverlay) { delete pGfxOverlay; pGfxOverlay=NULL; }
}
bool C4Object::ContainedControl(BYTE byCom)
{
// Check
if (!Contained) return false;
// Check if object is about to exit; if so, return
// dunno, maybe I should check all the commands, not just the first one?
if ((byCom == COM_Left || byCom == COM_Right) && Command)
if (Command->Command == C4CMD_Exit)
// hack: in structures only; not in vehicles
// they might have a pending Exit-command due to a down-control
if (Contained->Category & C4D_Structure)
return false; // or true? Currently it doesn't matter.
// get script function if defined
C4AulFunc* sf = Contained->Def->Script.GetSFunc(FormatString(PSF_ContainedControl,ComName(byCom)).getData());
// in old versions, do hardcoded actions first (until gwe3)
// new objects may overload them
C4Def *pCDef = Contained->Def;
bool fCallSfEarly = CompareVersion(pCDef->rC4XVer[0],pCDef->rC4XVer[1],pCDef->rC4XVer[2],pCDef->rC4XVer[3],4,9,1,3) >= 0;
bool result = false;
C4Player * pPlr = ::Players.Get(Controller);
if(fCallSfEarly)
{
if (sf && !!sf->Exec(Contained, &C4AulParSet(C4VObj(this)))) result = true;
// AutoStopControl: Also notify container about controlupdate
// Note Contained may be nulled now due to ContainedControl call
if(Contained && !(byCom & (COM_Single | COM_Double)) && pPlr->PrefControlStyle)
{
int32_t PressedComs = pPlr->PressedComs;
C4AulParSet set(C4VObj(this),
C4VInt(Coms2ComDir(PressedComs)),
C4VBool(!!(PressedComs & (1 << COM_Dig))),
C4VBool(!!(PressedComs & (1 << COM_Throw))));
Contained->Call(PSF_ContainedControlUpdate, &set);
}
}
if(result) return true;
// hardcoded actions
switch (byCom)
{
case COM_Down:
PlayerObjectCommand(Owner,C4CMD_Exit);
break;
case COM_Throw:
PlayerObjectCommand(Owner,C4CMD_Throw);
break;
case COM_Up:
if (ValidPlr(Contained->Base))
if (!Hostile(Owner,Contained->Base))
if (Game.C4S.Game.Realism.BaseFunctionality & BASEFUNC_Buy)
ActivateMenu(C4MN_Buy);
break;
case COM_Dig:
if (ValidPlr(Contained->Base))
if (!Hostile(Owner,Contained->Base))
if (Game.C4S.Game.Realism.BaseFunctionality & BASEFUNC_Sell)
ActivateMenu(C4MN_Sell);
break;
}
// Call container script if defined for old versions
if (!fCallSfEarly)
{
if(sf) sf->Exec(Contained, &C4AulParSet(C4VObj(this)));
if(Contained && !(byCom & (COM_Single | COM_Double)) && pPlr->PrefControlStyle)
{
int32_t PressedComs = pPlr->PressedComs;
C4AulParSet set(C4VObj(this),
C4VInt(Coms2ComDir(PressedComs)),
C4VBool(!!(PressedComs & (1 << COM_Dig))),
C4VBool(!!(PressedComs & (1 << COM_Throw))));
Contained->Call(PSF_ContainedControlUpdate, &set);
}
}
// Take/Take2
if(!sf || fCallSfEarly) switch (byCom)
{
case COM_Left:
PlayerObjectCommand(Owner,C4CMD_Take);
break;
case COM_Right:
PlayerObjectCommand(Owner,C4CMD_Take2);
break;
}
// Success
return true;
}
bool C4Object::CallControl(C4Player *pPlr, BYTE byCom, C4AulParSet *pPars)
{
assert(pPlr);
bool result = !!Call(FormatString(PSF_Control,ComName(byCom)).getData(),pPars);
// Call ControlUpdate when using Jump'n'Run control
if(pPlr->PrefControlStyle)
{
int32_t PressedComs = pPlr->PressedComs;
C4AulParSet set(pPars ? pPars->Par[0] : C4VObj(this),
C4VInt(Coms2ComDir(PressedComs)),
C4VBool(!!(PressedComs & (1 << COM_Dig))),
C4VBool(!!(PressedComs & (1 << COM_Throw))),
C4VBool(!!(PressedComs & (1 << COM_Special))),
C4VBool(!!(PressedComs & (1 << COM_Special2))));
Call(PSF_ControlUpdate, &set);
}
return result;
}
void C4Object::DirectCom(BYTE byCom, int32_t iData) // By player ObjectCom
{
#ifdef DEBUGREC_OBJCOM
C4RCObjectCom rc = { byCom, iData, Number };
AddDbgRec(RCT_ObjCom, &rc, sizeof(C4RCObjectCom));
#endif
// COM_Special and COM_Contents specifically bypass the menu and always go to the object
bool fBypassMenu = ((byCom == COM_Special) || (byCom == COM_Contents));
// Menu control
if (!fBypassMenu)
if (Menu && Menu->Control(byCom,iData)) return;
// Ignore any menu com leftover in control queue from closed menu
if (Inside(byCom,COM_MenuNavigation1,COM_MenuNavigation2)) return;
// Wether this is a KeyRelease-event
bool IsRelease = Inside(byCom, COM_ReleaseFirst, COM_ReleaseLast);
// Decrease NoCollectDelay
if (!(byCom & COM_Single) && !(byCom & COM_Double) && !IsRelease)
if (NoCollectDelay>0)
NoCollectDelay--;
// COM_Contents contents shift (data is target number (not ID!))
// contents shift must always be done to container object, which is not necessarily this
if (byCom==COM_Contents)
{
C4Object *pTarget = ::Objects.SafeObjectPointer(iData);
if (pTarget && pTarget->Contained)
pTarget->Contained->DirectComContents(pTarget, true);
return;
}
// Contained control (except specials - hey, doesn't catch singles or doubles)
if (Contained)
if (byCom!=COM_Special && byCom!=COM_Special2 && byCom!=COM_WheelUp && byCom!=COM_WheelDown)
{ ContainedControl(byCom); return; }
// Regular DirectCom clears commands
if (!(byCom & COM_Single) && !(byCom & COM_Double) && !IsRelease)
ClearCommands();
// Object script override
C4Player *pController;
if (pController = ::Players.Get(Controller))
if (CallControl(pController, byCom))
return;
// direct wheel control
if (byCom==COM_WheelUp || byCom==COM_WheelDown)
// scroll contents
{ ShiftContents(byCom==COM_WheelUp, true); return; }
// The Player updates Controller before calling this, so trust Players.Get will return it
if (pController && pController->PrefControlStyle)
{
AutoStopDirectCom(byCom, iData);
return;
}
// Control by procedure
switch (GetProcedure())
{
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_WALK:
switch (byCom)
{
case COM_Left: ObjectComMovement(this,COMD_Left); break;
case COM_Right: ObjectComMovement(this,COMD_Right); break;
case COM_Down: ObjectComMovement(this,COMD_Stop); break;
case COM_Up: ObjectComUp(this); break;
case COM_Down_D: ObjectComDownDouble(this); break;
case COM_Dig_S:
if (ObjectComDig(this))
{
Action.ComDir = (Action.Dir==DIR_Right) ? COMD_DownRight : COMD_DownLeft;
}
break;
case COM_Dig_D: ObjectComDigDouble(this); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_FLIGHT: case DFA_KNEEL: case DFA_THROW:
switch (byCom)
{
case COM_Left: ObjectComMovement(this,COMD_Left); break;
case COM_Right: ObjectComMovement(this,COMD_Right); break;
case COM_Down: ObjectComMovement(this,COMD_Stop); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_SCALE:
switch (byCom)
{
case COM_Left:
if (Action.Dir==DIR_Left) ObjectComMovement(this,COMD_Stop);
else { ObjectComMovement(this,COMD_Left); ObjectComLetGo(this,-1); }
break;
case COM_Right:
if (Action.Dir==DIR_Right) ObjectComMovement(this,COMD_Stop);
else { ObjectComMovement(this,COMD_Right); ObjectComLetGo(this,+1); }
break;
case COM_Up: ObjectComMovement(this,COMD_Up); break;
case COM_Down: ObjectComMovement(this,COMD_Down); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_HANGLE:
switch (byCom)
{
case COM_Left: ObjectComMovement(this,COMD_Left); break;
case COM_Right: ObjectComMovement(this,COMD_Right); break;
case COM_Up: ObjectComMovement(this,COMD_Stop); break;
case COM_Down: ObjectComLetGo(this,0); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_DIG:
switch (byCom)
{
case COM_Left: if (Inside<int32_t>(Action.ComDir,COMD_UpRight,COMD_Left)) Action.ComDir++; break;
case COM_Right: if (Inside<int32_t>(Action.ComDir,COMD_Right,COMD_UpLeft)) Action.ComDir--; break;
case COM_Down: ObjectComStop(this); break;
case COM_Dig_D: ObjectComDigDouble(this); break;
case COM_Dig_S: Action.Data = (!Action.Data); break; // Dig mat 2 object request
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_SWIM:
switch (byCom)
{
case COM_Left: ObjectComMovement(this,COMD_Left); break;
case COM_Right: ObjectComMovement(this,COMD_Right); break;
case COM_Up:
ObjectComMovement(this,COMD_Up);
ObjectComUp(this); break;
case COM_Down: ObjectComMovement(this,COMD_Down); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
case COM_Dig_D: ObjectComDigDouble(this); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_BRIDGE: case DFA_BUILD: case DFA_CHOP:
switch (byCom)
{
case COM_Down: ObjectComStop(this); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_FIGHT:
switch (byCom)
{
case COM_Left: ObjectComMovement(this,COMD_Left); break;
case COM_Right: ObjectComMovement(this,COMD_Right); break;
case COM_Down: ObjectComStop(this); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_PUSH:
{
bool fGrabControlOverload = false;
if(Action.Target)
{
// New grab-control model: objects version 4.95 or higher (CE)
// may overload control of grabbing clonks
C4Def *pTDef = Action.Target->Def;
if(CompareVersion(pTDef->rC4XVer[0],pTDef->rC4XVer[1],pTDef->rC4XVer[2],pTDef->rC4XVer[3],4,9,5,0) >= 0)
fGrabControlOverload = true;
}
// Call object control first in case it overloads
if (fGrabControlOverload)
if (Action.Target)
if (Action.Target->CallControl(pController, byCom, &C4AulParSet(C4VObj(this))))
return;
// Clonk direct control
switch (byCom)
{
case COM_Left: ObjectComMovement(this,COMD_Left); break;
case COM_Right: ObjectComMovement(this,COMD_Right); break;
case COM_Up:
// Target -> enter
if (ObjectComEnter(Action.Target))
ObjectComMovement(this,COMD_Stop);
// Else, comdir up for target straightening
else
ObjectComMovement(this,COMD_Up);
break;
case COM_Down: ObjectComMovement(this,COMD_Stop); break;
case COM_Down_D: ObjectComUnGrab(this); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break;
}
// Action target call control late for old objects
if (!fGrabControlOverload)
if (Action.Target)
Action.Target->CallControl(pController, byCom, &C4AulParSet(C4VObj(this)));
break;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
}
void C4Object::AutoStopDirectCom(BYTE byCom, int32_t iData) // By DirecCom
{
C4Player * pPlayer = ::Players.Get(Controller);
// Control by procedure
switch (GetProcedure())
{
case DFA_WALK:
switch (byCom)
{
case COM_Up: ObjectComUp(this); break;
case COM_Down:
// inhibit controldownsingle on freshly grabbed objects
if (ObjectComDownDouble(this))
pPlayer->LastCom = COM_None;
break;
case COM_Dig_S: ObjectComDig(this); break;
case COM_Dig_D: ObjectComDigDouble(this); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_FLIGHT:
switch (byCom)
{
case COM_Throw:
// Drop when pressing left, right or down
if (pPlayer->PressedComs & ((1<<COM_Left)|(1<<COM_Right)|(1<<COM_Down)))
PlayerObjectCommand(Owner,C4CMD_Drop);
else
// This will fail, but whatever.
PlayerObjectCommand(Owner,C4CMD_Throw);
break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_KNEEL: case DFA_THROW:
switch (byCom)
{
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_SCALE:
switch (byCom)
{
case COM_Left:
if (Action.Dir == DIR_Right) ObjectComLetGo(this,-1);
else AutoStopUpdateComDir();
break;
case COM_Right:
if (Action.Dir == DIR_Left) ObjectComLetGo(this,+1);
else AutoStopUpdateComDir();
break;
case COM_Dig: ObjectComLetGo(this,(Action.Dir == DIR_Left) ? +1 : -1);
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_HANGLE:
switch (byCom)
{
case COM_Down: ObjectComLetGo(this,0); break;
case COM_Dig: ObjectComLetGo(this,0); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_DIG:
switch (byCom)
{
// Dig mat 2 object request
case COM_Throw: case COM_Dig: Action.Data = (!Action.Data); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_SWIM:
switch (byCom)
{
case COM_Up:
AutoStopUpdateComDir();
ObjectComUp(this);
break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
case COM_Dig_D: ObjectComDigDouble(this); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_BRIDGE: case DFA_BUILD: case DFA_CHOP:
switch (byCom)
{
case COM_Down: ObjectComStop(this); break;
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_FIGHT:
switch (byCom)
{
case COM_Down: ObjectComStop(this); break;
default: AutoStopUpdateComDir();
}
break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
case DFA_PUSH:
{
bool fGrabControlOverload = false;
if(Action.Target)
{
// New grab-control model: objects version 4.95 or higher (CE)
// may overload control of grabbing clonks
C4Def *pTDef = Action.Target->Def;
if(CompareVersion(pTDef->rC4XVer[0],pTDef->rC4XVer[1],pTDef->rC4XVer[2],pTDef->rC4XVer[3],4,9,5,0) >= 0)
fGrabControlOverload = true;
// Call object control first in case it overloads
if (fGrabControlOverload)
{
if (Action.Target->CallControl(pPlayer, byCom, &C4AulParSet(C4VObj(this))))
{
return;
}
}
}
// Clonk direct control
switch (byCom)
{
case COM_Up:
// Target -> enter
if (ObjectComEnter(Action.Target))
ObjectComMovement(this,COMD_Stop);
// Else, comdir up for target straightening
else
AutoStopUpdateComDir();
break;
case COM_Down:
// FIXME: replace constants
// ComOrder(3) is COM_Down, ComOrder(11) is COM_Down_S and ComOrder(19) is COM_Down_D
if(Action.Target
&& !DrawCommandQuery(Controller, Action.Target->Def->Script, Action.Target->Def->Script.ControlMethod, 3)
&& !DrawCommandQuery(Controller, Action.Target->Def->Script, Action.Target->Def->Script.ControlMethod, 11)
&& !DrawCommandQuery(Controller, Action.Target->Def->Script, Action.Target->Def->Script.ControlMethod, 19))
{
ObjectComUnGrab(this);
}
break;
case COM_Down_D: ObjectComUnGrab(this); break;
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
default:
AutoStopUpdateComDir();
}
// Action target call control late for old objects
if (!fGrabControlOverload && Action.Target)
Action.Target->CallControl(pPlayer, byCom, &C4AulParSet(C4VObj(this)));
break;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
}
void C4Object::AutoStopUpdateComDir()
{
C4Player * pPlr = ::Players.Get(Controller);
if (!pPlr || pPlr->Cursor != this) return;
int32_t NewComDir = Coms2ComDir(pPlr->PressedComs);
if (Action.ComDir == NewComDir) return;
if (NewComDir == COMD_Stop && GetProcedure() == DFA_DIG)
{
ObjectComStop(this);
return;
}
ObjectComMovement(this, NewComDir);
}
bool C4Object::MenuCommand(const char *szCommand)
{
// Native script execution

View File

@ -217,7 +217,6 @@ class C4Object: public C4PropList
C4Object *ComposeContents(C4ID id);
bool MenuCommand(const char *szCommand);
bool CallControl(C4Player *pPlr, BYTE byCom, C4AulParSet *pPars = 0);
C4Value Call(const char *szFunctionCall, C4AulParSet *pPars = 0, bool fPassError = false);
@ -335,9 +334,6 @@ class C4Object: public C4PropList
bool TrainPhysical(C4PhysicalInfo::Offset mpiOffset, int32_t iTrainBy, int32_t iMaxTrain);
void SetName (const char *NewName = 0);
int32_t GetValue(C4Object *pInBase, int32_t iForPlayer);
void DirectCom(BYTE byCom, int32_t iData);
void AutoStopDirectCom(BYTE byCom, int32_t iData);
void AutoStopUpdateComDir();
bool BuyEnergy();
void AutoSellContents();
bool SetOwner(int32_t iOwner);

View File

@ -618,10 +618,10 @@ bool ObjectComPut(C4Object *cObj, C4Object *pTarget, C4Object *pThing)
if (pTarget!=cObj->Contained)
if (!(pTarget->Def->GrabPutGet & C4D_Grab_Put))
{
// Was meant to be a drop anyway
if (ValidPlr(cObj->Owner))
if (::Players.Get(cObj->Owner)->LastComDownDouble)
return ObjectComDrop(cObj, pThing);
// Was meant to be a drop anyway - probably obsolete as controls are being revised
//if (ValidPlr(cObj->Owner))
// if (Game.Players.Get(cObj->Owner)->LastComDownDouble)
// return ObjectComDrop(cObj, pThing);
// No grab put: fail
return false;
}
@ -1020,8 +1020,8 @@ bool PlayerObjectCommand(int32_t plr, int32_t cmdf, C4Object *pTarget, int32_t t
if (cmdf==C4CMD_Throw)
{
bool fConvertToDrop = false;
// Drop on down-down-throw (classic)
if (pPlr->LastComDownDouble)
// Drop on down-down-throw (classic) - obsolete?
/*if (pPlr->LastComDownDouble)
{
fConvertToDrop = true;
// Dropping one object automatically reenables LastComDownDouble to
@ -1032,7 +1032,7 @@ bool PlayerObjectCommand(int32_t plr, int32_t cmdf, C4Object *pTarget, int32_t t
pPlr->LastComDownDouble = C4DoubleClick;
}
// Jump'n'Run: Drop on combined Down/Left/Right+Throw
if (pPlr->PrefControlStyle && (pPlr->PressedComs & (1 << COM_Down))) fConvertToDrop = true;
if (pPlr->PrefControlStyle && (pPlr->PressedComs & (1 << COM_Down))) fConvertToDrop = true;*/
if (fConvertToDrop) return pPlr->ObjectCommand(C4CMD_Drop,pTarget,tx,ty,NULL,C4VNull,iAddMode);
}
// Route to player

View File

@ -440,20 +440,20 @@ void C4ObjectMenu::Execute()
void C4ObjectMenu::OnUserSelectItem(int32_t Player, int32_t iIndex)
{
// queue....
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,COM_MenuSelect,iIndex | C4MN_AdjustPosition));
// queue.... 2do
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,Game.PlayerControlDefs.InternalCons.CON_MenuSelect,iIndex | C4MN_AdjustPosition));
}
void C4ObjectMenu::OnUserEnter(int32_t Player, int32_t iIndex, bool fRight)
{
// object menu: Through queue
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,fRight ? COM_MenuEnterAll : COM_MenuEnter,iIndex));
// object menu: Through queue 2do
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,fRight ? Game.PlayerControlDefs.InternalCons.CON_MenuEnterAll : Game.PlayerControlDefs.InternalCons.CON_MenuEnter,iIndex));
}
void C4ObjectMenu::OnUserClose()
{
// Queue
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(::MouseControl.GetPlayer(),COM_MenuClose,0));
// Queue 2do
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(::MouseControl.GetPlayer(),Game.PlayerControlDefs.InternalCons.CON_MenuClose,0));
}
bool C4ObjectMenu::IsReadOnly()

View File

@ -43,8 +43,7 @@ void C4LSector::Init(int ix, int iy)
void C4LSector::Clear()
{
// clear objects
Objects.Clear();
ObjectShapes.Clear();
ClearObjects();
}
void C4LSector::CompileFunc(StdCompiler *pComp)
@ -55,6 +54,13 @@ void C4LSector::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(ObjectShapes, "ObjectShapes"));
}
void C4LSector::ClearObjects()
{
// clear objects
Objects.Clear();
ObjectShapes.Clear();
}
/* sector map */
void C4LSectors::Init(int iWdt, int iHgt)
@ -226,6 +232,15 @@ bool C4LSectors::CheckSort()
return true;
}
void C4LSectors::ClearObjects()
{
if (Sectors)
{
for (int cnt=0; cnt<Size; cnt++) Sectors[cnt].ClearObjects();
}
SectorOut.ClearObjects();
}
/* landscape area */
bool C4LArea::operator == (const C4LArea &Area) const

View File

@ -51,6 +51,7 @@ class C4LSector
C4ObjectList ObjectShapes; // objects with shapes that overlap this sector
void CompileFunc(StdCompiler *pComp);
void ClearObjects(); // remove all objects from object lists
friend class C4LSectors;
};
@ -73,6 +74,7 @@ class C4LSectors
void Add(C4Object *pObj, C4ObjectList *pMainList);
void Update(C4Object *pObj, C4ObjectList *pMainList); // does not update object order!
void Remove(C4Object *pObj);
void ClearObjects(); // remove all objects from object lists
void AssertObjectNotInList(C4Object *pObj); // searches all sector lists for object, and assert if it's inside a list

View File

@ -1091,7 +1091,6 @@ void C4Player::Default()
ControlCount = ActionCount = 0;
LastControlType = PCID_None;
LastControlID = 0;
PressedComs = 0;
pMsgBoardQuery = NULL;
pGamepad = NULL;
NoEliminationCheck = false;
@ -1219,26 +1218,7 @@ bool C4Player::MakeCrewMember(C4Object *pObj, bool fForceInfo, bool fDoCalls)
void C4Player::ExecuteControl()
{
// LastCom
if (LastCom != COM_None)
{
// Advance delay counter
LastComDelay++;
// Check for COM_Single com (after delay)
if (LastComDelay > C4DoubleClick)
{
// Pass additional COM_Single com (unless it already was a single com in the first place)
if (!(LastCom & COM_Single))
DirectCom(LastCom | COM_Single, 0); // Currently, com data is not stored for single coms...
LastCom = COM_None;
LastComDelay = 0;
}
}
// LastComDownDouble
if (LastComDownDouble>0) LastComDownDouble--;
Control.Execute();
}
void C4Player::AdjustCursorCommand()
@ -1385,30 +1365,6 @@ void C4Player::UpdateSelectionToggleStatus()
CursorToggled=0;
}
bool C4Player::ObjectCom(BYTE byCom, int32_t iData) // By DirectCom
{
if (Eliminated) return false;
#ifdef DEBUGREC_OBJCOM
C4RCObjectCom rc = { byCom, iData, Number };
AddDbgRec(RCT_PlrCom, &rc, sizeof(C4RCObjectCom));
#endif
// Hide startup
ShowStartup=false;
// If regular com, update cursor & selection status
if (!(byCom & COM_Single) && !(byCom & COM_Double) && (byCom < COM_ReleaseFirst || byCom > COM_ReleaseLast))
UpdateSelectionToggleStatus();
// Apply direct com to cursor object
if (Cursor)
{
// update controller
Cursor->Controller = Number;
// send com
Cursor->DirectCom(byCom,iData);
}
// Done
return true;
}
bool C4Player::ObjectCommand(int32_t iCommand, C4Object *pTarget, int32_t iX, int32_t iY, C4Object *pTarget2, C4Value iData, int32_t iMode)
{
// Eliminated
@ -1465,72 +1421,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::DirectCom(BYTE byCom, int32_t iData) // By InCom or ExecuteControl
{
switch (byCom)
{
case COM_CursorLeft: CursorLeft(); break;
case COM_CursorRight: CursorRight(); break;
case COM_CursorToggle: CursorToggle(); break;
case COM_CursorToggle_D: SelectAllCrew(); break;
default: ObjectCom(byCom,iData); break;
}
}
void C4Player::InCom(BYTE byCom, int32_t iData)
{
#ifdef DEBUGREC_OBJCOM
C4RCObjectCom rc = { byCom, iData, Number };
AddDbgRec(RCT_PlrInCom, &rc, sizeof(C4RCObjectCom));
#endif
// Cursor object menu active: convert regular com to menu com
if (Cursor) if (Cursor->Menu)
{
int32_t iCom = byCom;
Cursor->Menu->ConvertCom(iCom,iData, false);
byCom = iCom;
}
// Menu control: no single/double processing
if (Inside(byCom,COM_MenuFirst,COM_MenuLast))
{ DirectCom(byCom,iData); return; }
// Ignore KeyRelease for Single/Double
if (!Inside(byCom, COM_ReleaseFirst, COM_ReleaseLast))
{
// Reset view
ResetCursorView();
// Update state
if (Inside<int>(byCom, COM_ReleaseFirst - 16, COM_ReleaseLast - 16))
PressedComs |= 1 << byCom;
// Check LastCom buffer for prior COM_Single
if (LastCom!=COM_None)
if (LastCom!=byCom)
{
DirectCom(LastCom | COM_Single,iData);
// AutoStopControl uses a single COM_Down instead of DOM_Down_D for drop
// So a COM_Down_S does what a COM_Down_D normally does, if generated by another key
// instead of a timeout
if (PrefControlStyle && LastCom == COM_Down) LastComDownDouble = C4DoubleClick;
}
// Check LastCom buffer for COM_Double
if (LastCom==byCom) byCom|=COM_Double;
// LastCom/Del process
// this is set before issuing the DirectCom, so DirectCom-scripts may delete it
LastCom=byCom; LastComDelay=0;
}
else
{
// Update state
if (Inside(byCom, COM_ReleaseFirst, COM_ReleaseLast))
PressedComs &= ~(1 << (byCom - 16));
}
// Pass regular/COM_Double byCom to player
DirectCom(byCom,iData);
// LastComDownDouble process
if (byCom == COM_Down_D) LastComDownDouble = C4DoubleClick;
}
void C4Player::CompileFunc(StdCompiler *pComp)
void C4Player::CompileFunc(StdCompiler *pComp, bool fExact)
{
assert(ID);
@ -1544,7 +1435,7 @@ void C4Player::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(Evaluated, "Evaluated", false));
pComp->Value(mkNamingAdapt(Color, "Color", -1));
pComp->Value(mkNamingAdapt(ColorDw, "ColorDw", 0u));
pComp->Value(mkNamingAdapt(Control, "Control", 0));
pComp->Value(mkNamingAdapt(ControlSet, "Control", 0));
pComp->Value(mkNamingAdapt(MouseControl, "MouseControl", 0));
pComp->Value(mkNamingAdapt(Position, "Position", 0));
pComp->Value(mkNamingAdapt(ViewMode, "ViewMode", C4PVM_Cursor));
@ -1556,8 +1447,8 @@ void C4Player::CompileFunc(StdCompiler *pComp)
bool bForceFogOfWar = false;
pComp->Value(mkNamingAdapt(bForceFogOfWar, "ForceFogOfWar", false));
pComp->Value(mkNamingAdapt(ShowStartup, "ShowStartup", false));
pComp->Value(mkNamingAdapt(ShowControl, "ShowControl", false));
pComp->Value(mkNamingAdapt(ShowControlPos, "ShowControlPos", false));
pComp->Value(mkNamingAdapt(ShowControl, "ShowControl", 0));
pComp->Value(mkNamingAdapt(ShowControlPos, "ShowControlPos", 0));
pComp->Value(mkNamingAdapt(Wealth, "Wealth", 0));
pComp->Value(mkNamingAdapt(Points, "Points", 0));
pComp->Value(mkNamingAdapt(Value, "Value", 0));
@ -1573,10 +1464,6 @@ void C4Player::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt((int32_t&)Cursor, "Cursor", 0));
pComp->Value(mkNamingAdapt((int32_t&)ViewCursor,"ViewCursor", 0));
pComp->Value(mkNamingAdapt((int32_t&)Captain, "Captain", 0));
pComp->Value(mkNamingAdapt(LastCom, "LastCom", 0));
pComp->Value(mkNamingAdapt(LastComDelay, "LastComDel", 0));
pComp->Value(mkNamingAdapt(PressedComs, "PressedComs", 0));
pComp->Value(mkNamingAdapt(LastComDownDouble, "LastComDownDouble", 0));
pComp->Value(mkNamingAdapt(CursorSelection, "CursorSelection", 0));
pComp->Value(mkNamingAdapt(CursorToggled, "CursorToggled", 0));
pComp->Value(mkNamingAdapt(MessageStatus, "MessageStatus", 0));
@ -1588,6 +1475,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)
@ -1598,9 +1488,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;
@ -1682,7 +1573,7 @@ void C4Player::DefaultRuntimeData()
AtClient=C4ClientIDUnknown;
SCopy("Local",AtClientName);
Color=-1;
Control=C4P_Control_None;
ControlSet=C4P_Control_None;
MouseControl=false;
Position=-1;
PlrStartIndex=0;
@ -1704,9 +1595,6 @@ void C4Player::DefaultRuntimeData()
Cursor=ViewCursor=NULL;
SelectCount=0;
SelectFlash=CursorFlash=30;
LastCom=0;
LastComDelay=0;
LastComDownDouble=0;
CursorSelection=CursorToggled=0;
MessageStatus=0;
MessageBuf[0]=0;
@ -1846,7 +1734,7 @@ void C4Player::InitControl()
if (!GetInfo() || GetInfo()->GetType() == C4PT_User)
LocalControl=true;
// Set control
Control=C4P_Control_None;
ControlSet=C4P_Control_None;
// Preferred control
int32_t iControl = PrefControl;
// gamepad control safety
@ -1858,32 +1746,35 @@ void C4Player::InitControl()
if (::Players.ControlTaken(iControl))
{
// Preferred control taken, search for available keyboard control
for (iControl=C4P_Control_Keyboard1; iControl<=C4P_Control_Keyboard4; iControl++)
for (iControl=C4P_Control_Keyboard1; iControl<=C4P_Control_Keyboard2; iControl++)
if (!::Players.ControlTaken(iControl)) // Available control found
break;
// No available control found
if (iControl>C4P_Control_Keyboard4)
if (iControl>C4P_Control_Keyboard2)
iControl=C4P_Control_None;
}
// Set control
Control=iControl;
ControlSet=iControl;
// init gamepad
if (pGamepad) { delete pGamepad; pGamepad=NULL; }
if (Inside<int32_t>(Control, C4P_Control_GamePad1, C4P_Control_GamePadMax))
if (Inside<int32_t>(ControlSet, C4P_Control_GamePad1, C4P_Control_GamePadMax))
{
pGamepad = new C4GamePadOpener(Control - C4P_Control_GamePad1);
pGamepad = new C4GamePadOpener(ControlSet - C4P_Control_GamePad1);
}
// Mouse
if (PrefMouse && !::Control.isReplay())
if (!Game.C4S.Head.DisableMouse)
if (Inside<int32_t>(Control, C4P_Control_Keyboard1, C4P_Control_GamePadMax))
if (Inside<int32_t>(ControlSet, C4P_Control_Keyboard1, C4P_Control_GamePadMax))
if (!::Players.MouseControlTaken())
MouseControl=true;
// no controls issued yet
ControlCount = ActionCount = 0;
LastControlType = PCID_None;
LastControlID = 0;
PressedComs = 0;
// init control callbacks
StdStrBuf sKeysetName;
sKeysetName.Format("Keyboard%d%s", ControlSet, PrefControlStyle ? "" : "Classic");
Control.RegisterKeyset(Number, Game.PlayerControlAssignmentSets.GetSetByName(sKeysetName.getData()));
}
int igOffX, igOffY;

View File

@ -26,6 +26,7 @@
#include <C4ObjectInfoList.h>
#include <C4InfoCore.h>
#include <C4ObjectList.h>
#include <C4PlayerControl.h>
const int32_t C4PVM_Cursor = 0,
C4PVM_Target = 1,
@ -72,7 +73,7 @@ class C4Player: public C4PlayerInfoCore
int32_t Team; // team ID - may be 0 for no teams
int32_t Color; // OldGfx color index
uint32_t ColorDw; // color as DWord for newgfx
int32_t Control;
int32_t ControlSet;
int32_t MouseControl;
int32_t Position;
int32_t PlrStartIndex;
@ -114,13 +115,10 @@ class C4Player: public C4PlayerInfoCore
C4IDList Knowledge;
C4IDList Magic;
// Control
C4PlayerControl Control;
C4Object *Cursor, *ViewCursor;
int32_t SelectCount;
int32_t SelectFlash,CursorFlash;
int32_t LastCom;
int32_t LastComDelay;
int32_t LastComDownDouble;
int32_t PressedComs;
int32_t CursorSelection,CursorToggled;
class C4GamePadOpener *pGamepad;
// Message
@ -171,9 +169,6 @@ class C4Player: public C4PlayerInfoCore
void CursorToggle();
void SelectAllCrew();
void UpdateSelectionToggleStatus();
void DirectCom(BYTE byCom, int32_t iData);
void InCom(BYTE byCom, int32_t iData);
bool ObjectCom(BYTE byCom, int32_t iData);
bool ObjectCommand(int32_t iCommand, C4Object *pTarget, int32_t iTx, int32_t iTy, C4Object *pTarget2=NULL, C4Value iData=C4VNull, int32_t iAddMode=C4P_Command_Set);
void ObjectCommand2Obj(C4Object *cObj, int32_t iCommand, C4Object *pTarget, int32_t iX, int32_t iY, C4Object *pTarget2, C4Value iData, int32_t iMode);
bool DoPoints(int32_t iChange);
@ -192,7 +187,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

@ -149,7 +149,7 @@ int C4PlayerList::CheckColorDw(DWORD dwColor, C4Player *pExclude)
bool C4PlayerList::ControlTaken(int iControl) const
{
for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next)
if (pPlr->Control==iControl)
if (pPlr->ControlSet==iControl)
if (pPlr->LocalControl)
return true;
return false;
@ -193,7 +193,7 @@ C4Player* C4PlayerList::GetLocalByKbdSet(int iKbdSet) const
{
for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next)
if (pPlr->LocalControl)
if (pPlr->Control==iKbdSet)
if (pPlr->ControlSet==iKbdSet)
return pPlr;
return NULL;
}
@ -653,7 +653,7 @@ void C4PlayerList::DenumeratePointers()
int C4PlayerList::ControlTakenBy(int iControl) const
{
for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next)
if (pPlr->Control==iControl)
if (pPlr->ControlSet==iControl)
if (pPlr->LocalControl)
return pPlr->Number;
return NO_OWNER;

View File

@ -761,6 +761,12 @@ static bool FnSetActionData(C4AulObjectContext *cthr, long iData)
return true;
}
static long FnGetActionData(C4AulObjectContext *cthr)
{
// get data
return cthr->Obj->Action.Data;
}
static C4Void FnSetComDir(C4AulObjectContext *cthr, long ncomdir)
{
cthr->Obj->Action.ComDir=ncomdir;
@ -2438,24 +2444,6 @@ static bool FnDoHomebaseProduction(C4AulContext *cthr, long iPlr, C4ID id, long
return ::Players.Get(iPlr)->HomeBaseProduction.SetIDCount(id,iLastcount+iChange,true);
}
static long FnGetPlrDownDouble(C4AulContext *cthr, long iPlr)
{
if (!ValidPlr(iPlr)) return false;
return ::Players.Get(iPlr)->LastComDownDouble;
}
static bool FnClearLastPlrCom(C4AulContext *cthr, long iPlr)
{
// get player
C4Player *pPlr = ::Players.Get(iPlr);
if (!pPlr) return false;
// reset last coms
pPlr->LastCom = COM_None;
pPlr->LastComDownDouble = 0;
// done, success
return true;
}
static bool FnSetPlrKnowledge(C4AulContext *cthr, long iPlr, C4ID id, bool fRemove)
{
C4Player *pPlr=::Players.Get(iPlr);
@ -3705,7 +3693,7 @@ static C4Value FnGetPlayerVal(C4AulContext* cthr, C4Value* strEntry_C4V, C4Value
C4Player* pPlayer = ::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)
@ -5455,6 +5443,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 = ::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
@ -5635,6 +5649,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "Bubble", FnBubble);
AddFunc(pEngine, "SetAction", FnSetAction);
AddFunc(pEngine, "SetActionData", FnSetActionData);
AddFunc(pEngine, "GetActionData", FnGetActionData);
AddFunc(pEngine, "SetBridgeActionData", FnSetBridgeActionData);
AddFunc(pEngine, "GetAction", FnGetAction);
AddFunc(pEngine, "GetActTime", FnGetActTime);
@ -5717,8 +5732,6 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "SetPlrView", FnSetPlrView);
AddFunc(pEngine, "SetPlrKnowledge", FnSetPlrKnowledge);
AddFunc(pEngine, "SetPlrMagic", FnSetPlrMagic);
AddFunc(pEngine, "GetPlrDownDouble", FnGetPlrDownDouble);
AddFunc(pEngine, "ClearLastPlrCom", FnClearLastPlrCom);
AddFunc(pEngine, "GetPlrViewMode", FnGetPlrViewMode);
AddFunc(pEngine, "GetPlrView", FnGetPlrView);
AddFunc(pEngine, "GetWealth", FnGetWealth);
@ -5926,6 +5939,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "LocateFunc", FnLocateFunc);
AddFunc(pEngine, "PathFree", FnPathFree);
AddFunc(pEngine, "SetNextMission", FnSetNextMission);
AddFunc(pEngine, "GetPlayerControlState", FnGetPlayerControlState);
//FIXME new C4AulDefCastFunc(pEngine, "ScoreboardCol", C4V_C4ID, C4V_Int);
AddFunc(pEngine, "goto", Fn_goto);

View File

@ -153,6 +153,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, bool fReleased
#define PSF_Definition "~Definition" // proplist definition
// Fx%s is automatically prefixed

View File

@ -28,7 +28,9 @@
#endif
#ifdef _WIN32
#ifndef _WIN32_IE
#define _WIN32_IE 0x0400
#endif
#include <shlobj.h>
#ifndef CSIDL_MYPICTURES
#define CSIDL_MYPICTURES 0x0027

View File

@ -1167,7 +1167,7 @@ namespace C4GUI {
private:
enum CursorOperation { COP_BACK, COP_DELETE, COP_LEFT, COP_RIGHT, COP_HOME, COP_END, };
bool KeyCursorOp(C4KeyCodeEx key, CursorOperation op);
bool KeyCursorOp(const C4KeyCodeEx &key, CursorOperation op);
bool KeyEnter();
bool KeyCopy() { Copy(); return true; }
bool KeyPaste() { Paste(); return true; }
@ -1675,7 +1675,7 @@ namespace C4GUI {
bool KeyBack();
bool KeyAbort();
bool KeyConfirm();
bool KeyHotkey(C4KeyCodeEx key);
bool KeyHotkey(const C4KeyCodeEx &key);
private:
static int32_t iGlobalMenuIndex;
@ -2009,7 +2009,7 @@ namespace C4GUI {
virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse. forwards to child controls
private:
bool KeyHotkey(C4KeyCodeEx key);
bool KeyHotkey(const C4KeyCodeEx &key);
bool KeyFocusDefault();
public:
@ -2333,7 +2333,7 @@ namespace C4GUI {
{
private:
typedef C4KeyCBPassKey<TargetClass> Base;
typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key);
typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key);
public:
DlgKeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL)
: Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {}
@ -2370,7 +2370,7 @@ namespace C4GUI {
{
private:
typedef C4KeyCBExPassKey<TargetClass, ParameterType> Base;
typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key, ParameterType par);
typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, ParameterType par);
public:
ControlKeyCBExPassKey(TargetClass &rTarget, const ParameterType &rPar, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL)
: Base(rTarget, rPar, pFuncDown, pFuncUp, pFuncPressed) {}

View File

@ -600,7 +600,7 @@ bool Dialog::CharIn(const char * c)
return false;
}
bool Dialog::KeyHotkey(C4KeyCodeEx key)
bool Dialog::KeyHotkey(const C4KeyCodeEx &key)
{
WORD wKey = WORD(key.Key);
// do hotkey procs for standard alphanumerics only

View File

@ -400,7 +400,7 @@ bool Edit::KeyEnter()
return true;
}
bool Edit::KeyCursorOp(C4KeyCodeEx key, CursorOperation op)
bool Edit::KeyCursorOp(const C4KeyCodeEx &key, CursorOperation op)
{
bool fShift = !!(key.dwShift & KEYS_Shift);
bool fCtrl = !!(key.dwShift & KEYS_Control);

View File

@ -299,7 +299,8 @@ bool ContextMenu::KeyConfirm()
DoOK();
return true;
}
bool ContextMenu::KeyHotkey(C4KeyCodeEx key)
bool ContextMenu::KeyHotkey(const C4KeyCodeEx &key)
{
// not if focus is in submenu
if (pSubmenu) return false;

View File

@ -445,7 +445,7 @@ StdStrBuf C4KeyCodeEx::ToString(bool fHumanReadable, bool fShort)
/* ----------------- C4KeyCodeEx ------------------ */
void C4KeyCodeEx::CompileFunc(StdCompiler *pComp)
void C4KeyCodeEx::CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBufIfUndefined)
{
if (pComp->isCompiler())
{
@ -468,7 +468,18 @@ void C4KeyCodeEx::CompileFunc(StdCompiler *pComp)
// last section: convert to key code
C4KeyCode eCode = String2KeyCode(sCode);
if (eCode == KEY_Undefined)
pComp->excCorrupt("undefined key code: %s", sCode.getData());
{
if (pOutBufIfUndefined)
{
// unknown key, but an output buffer for unknown keys was provided. Use it.
pOutBufIfUndefined->Take(sCode);
eCode = KEY_Default;
}
else
{
pComp->excCorrupt("undefined key code: %s", sCode.getData());
}
}
dwShift = dwSetShift;
Key = eCode;
}
@ -487,9 +498,24 @@ void C4KeyCodeEx::CompileFunc(StdCompiler *pComp)
}
}
void C4KeyEventData::CompileFunc(StdCompiler *pComp)
{
pComp->Value(iStrength);
pComp->Seperator();
pComp->Value(x);
pComp->Seperator();
pComp->Value(y);
}
bool C4KeyEventData::operator ==(const struct C4KeyEventData &cmp) const
{
return iStrength == cmp.iStrength
&& x == cmp.x && y == cmp.y;
}
/* ----------------- C4CustomKey------------------ */
C4CustomKey::C4CustomKey(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
C4CustomKey::C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
: Scope(Scope), Name(), uiPriority(uiPriority), iRef(0)
{
// generate code
@ -517,7 +543,7 @@ C4CustomKey::C4CustomKey(const CodeList &rDefCodes, const char *szName, C4KeySco
}
}
C4CustomKey::C4CustomKey(C4KeyCodeEx Code, const StdStrBuf &rName)
C4CustomKey::C4CustomKey(const C4KeyCodeEx &Code, const StdStrBuf &rName)
: Codes(), DefaultCodes(), Scope(KEYSCOPE_None), Name(), uiPriority(PRIO_None), iRef(0)
{
// ctor for custom key override
@ -598,7 +624,7 @@ bool C4CustomKey::Execute(C4KeyEventType eEv, C4KeyCodeEx key)
/* ----------------- C4KeyBinding ------------------ */
C4KeyBinding::C4KeyBinding(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
C4KeyBinding::C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
: C4CustomKey(DefCode, szName, Scope, pCallback, uiPriority)
{
// self holds a ref
@ -633,6 +659,7 @@ bool C4KeyboardInput::IsValid = false;
void C4KeyboardInput::Clear()
{
LastKeyExtraData = C4KeyEventData();
// release all keys - name map is guarantueed to contain them all
for (KeyNameMap::const_iterator i = KeysByName.begin(); i != KeysByName.end(); ++i)
i->second->Deref();
@ -741,6 +768,9 @@ void C4KeyboardInput::UnregisterKeyBinding(C4CustomKey *pUnregKey)
bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope)
{
// store last-key-info
LastKeyExtraData.iStrength = (InEvent != KEYEV_Up) * 100;
LastKeyExtraData.x = LastKeyExtraData.y = 0;
// check all key events generated by this key: First the keycode itself, then any more generic key events like KEY_Any
const int32_t iKeyRangeMax = 5;
int32_t iKeyRangeCnt=0, j;

View File

@ -22,6 +22,8 @@
#define INC_C4KeyboardInput
#include <cctype>
#include <vector>
#include <map>
// key context classifications
enum C4KeyScope
@ -166,12 +168,23 @@ struct C4KeyCodeEx
return Key == v2.Key && dwShift == v2.dwShift;
}
void CompileFunc(StdCompiler *pComp);
void CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBufIfUndefined=NULL);
C4KeyCodeEx(C4KeyCode Key = KEY_Default, C4KeyShiftState Shift = KEYS_None, bool fIsRepeated = false)
: Key(Key), dwShift(Shift), fRepeated(fIsRepeated) {}
bool IsRepeated() { return fRepeated; }
bool IsRepeated() const { return fRepeated; }
};
// extra data associated with a key event
struct C4KeyEventData
{
int32_t iStrength; // pressure between 0 and 100 (100 for nomal keypress)
int32_t x,y; // position for mouse event
C4KeyEventData() : iStrength(0), x(0), y(0) {}
C4KeyEventData(int32_t iStrength, int32_t x, int32_t y) : iStrength(iStrength), x(x), y(y) {}
void CompileFunc(StdCompiler *pComp);
bool operator ==(const struct C4KeyEventData &cmp) const;
};
// callback interface
@ -183,7 +196,7 @@ class C4KeyboardCallbackInterface
class C4CustomKey *pOriginalKey;
public:
virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv) = 0; // return true if processed
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) = 0; // return true if processed
friend class C4KeyboardMapping;
@ -208,7 +221,7 @@ template <class TargetClass> class C4KeyCB : public C4KeyboardCallbackInterface
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
protected:
virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv)
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv)
{
if (!CheckCondition()) return false;
switch (eEv)
@ -231,14 +244,14 @@ template <class TargetClass> class C4KeyCB : public C4KeyboardCallbackInterface
template <class TargetClass> class C4KeyCBPassKey : public C4KeyboardCallbackInterface
{
public:
typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key);
typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key);
protected:
TargetClass &rTarget;
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
protected:
virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv)
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv)
{
if (!CheckCondition()) return false;
switch (eEv)
@ -269,7 +282,7 @@ template <class TargetClass, class ParameterType> class C4KeyCBEx : public C4Key
ParameterType par;
protected:
virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv)
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv)
{
if (!CheckCondition()) return false;
switch (eEv)
@ -291,7 +304,7 @@ template <class TargetClass, class ParameterType> class C4KeyCBEx : public C4Key
template <class TargetClass, class ParameterType> class C4KeyCBExPassKey : public C4KeyboardCallbackInterface
{
public:
typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key, ParameterType par);
typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, ParameterType par);
protected:
TargetClass &rTarget;
@ -299,7 +312,7 @@ template <class TargetClass, class ParameterType> class C4KeyCBExPassKey : publi
ParameterType par;
protected:
virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv)
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv)
{
if (!CheckCondition()) return false;
switch (eEv)
@ -350,9 +363,9 @@ class C4CustomKey
int iRef;
public:
C4CustomKey(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
C4CustomKey(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key with multiple possible keys assigned
C4CustomKey(C4KeyCodeEx Code, const StdStrBuf &rName); // ctor for single custom key override
C4CustomKey(const C4KeyCodeEx &Code, const StdStrBuf &rName); // ctor for single custom key override
C4CustomKey(const C4CustomKey &rCpy, bool fCopyCallbacks);
virtual ~C4CustomKey(); // dtor
@ -376,7 +389,7 @@ class C4CustomKey
class C4KeyBinding : protected C4CustomKey
{
public:
C4KeyBinding(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
C4KeyBinding(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
~C4KeyBinding();
};
@ -396,6 +409,7 @@ class C4KeyboardInput
// mapping of all keys by code and name
KeyCodeMap KeysByCode;
KeyNameMap KeysByName;
C4KeyEventData LastKeyExtraData;
public:
static bool IsValid; // global var to fix any deinitialization orders of key map and static keys
@ -421,6 +435,7 @@ class C4KeyboardInput
C4CustomKey *GetKeyByName(const char *szKeyName);
StdStrBuf GetKeyCodeNameByKeyName(const char *szKeyName, bool fShort = false, int32_t iIndex = 0);
const C4KeyEventData &GetLastKeyExtraData() const { return LastKeyExtraData; }
};
// keyboardinput-initializer-helper

View File

@ -186,7 +186,7 @@ bool C4ChatInputDialog::KeyHistoryUpDown(bool fUp)
return true;
}
bool C4ChatInputDialog::KeyPlrControl(C4KeyCodeEx key)
bool C4ChatInputDialog::KeyPlrControl(const C4KeyCodeEx &key)
{
// Control pressed while doing this key: Reroute this key as a player-control
Game.DoKeyboardInput(WORD(key.Key), KEYEV_Down, !!(key.dwShift & KEYS_Alt), false, !!(key.dwShift & KEYS_Shift), key.IsRepeated(), NULL, true);
@ -194,7 +194,7 @@ bool C4ChatInputDialog::KeyPlrControl(C4KeyCodeEx key)
return true;
}
bool C4ChatInputDialog::KeyGamepadControlDown(C4KeyCodeEx key)
bool C4ChatInputDialog::KeyGamepadControlDown(const C4KeyCodeEx &key)
{
// filter gamepad control
if (!Key_IsGamepad(key.Key)) return false;
@ -203,7 +203,7 @@ bool C4ChatInputDialog::KeyGamepadControlDown(C4KeyCodeEx key)
return true;
}
bool C4ChatInputDialog::KeyGamepadControlUp(C4KeyCodeEx key)
bool C4ChatInputDialog::KeyGamepadControlUp(const C4KeyCodeEx &key)
{
// filter gamepad control
if (!Key_IsGamepad(key.Key)) return false;
@ -212,7 +212,7 @@ bool C4ChatInputDialog::KeyGamepadControlUp(C4KeyCodeEx key)
return true;
}
bool C4ChatInputDialog::KeyGamepadControlPressed(C4KeyCodeEx key)
bool C4ChatInputDialog::KeyGamepadControlPressed(const C4KeyCodeEx &key)
{
// filter gamepad control
if (!Key_IsGamepad(key.Key)) return false;

View File

@ -47,10 +47,10 @@ class C4ChatInputDialog : public C4GUI::InputDialog
private:
bool KeyHistoryUpDown(bool fUp);
bool KeyCompleteNick(); // complete nick at cursor pos of edit
bool KeyPlrControl(C4KeyCodeEx key);
bool KeyGamepadControlDown(C4KeyCodeEx key);
bool KeyGamepadControlUp(C4KeyCodeEx key);
bool KeyGamepadControlPressed(C4KeyCodeEx key);
bool KeyPlrControl(const C4KeyCodeEx &key);
bool KeyGamepadControlDown(const C4KeyCodeEx &key);
bool KeyGamepadControlUp(const C4KeyCodeEx &key);
bool KeyGamepadControlPressed(const C4KeyCodeEx &key);
bool KeyBackspaceClose(); // close if chat text box is empty (on backspace)
protected:

View File

@ -1075,8 +1075,9 @@ void C4MouseControl::Wheel(DWORD dwFlags)
// Ctrl + Wheel: pass to player control (might be used for inventory or such)
else
{
if(iDelta > 0) Game.LocalPlayerControl(Player, COM_WheelUp);
if(iDelta < 0) Game.LocalPlayerControl(Player, COM_WheelDown);
// 2do
//if(iDelta > 0) Game.LocalPlayerControl(Player, COM_WheelUp);
//if(iDelta < 0) Game.LocalPlayerControl(Player, COM_WheelDown);
}
}

View File

@ -177,7 +177,7 @@ C4StartupOptionsDlg::KeySelDialog::~KeySelDialog()
delete pKeyListener;
}
bool C4StartupOptionsDlg::KeySelDialog::KeyDown(C4KeyCodeEx key)
bool C4StartupOptionsDlg::KeySelDialog::KeyDown(const C4KeyCodeEx &key)
{
// check if key is valid for this set
// do not mix gamepad and keyboard keys
@ -1358,6 +1358,7 @@ bool C4StartupOptionsDlg::OnLangComboSelChange(C4GUI::ComboBox *pForCombo, int32
Config.General.Language[2] = '\0';
UpdateLanguage();
Languages.LoadLanguage(Config.General.LanguageEx);
Game.UpdateLanguage();
// recreate everything to reflect language changes
RecreateDialog(true);
return true;

View File

@ -172,7 +172,7 @@ class C4StartupOptionsDlg : public C4StartupDlg
int32_t iCtrlSet;
protected:
bool KeyDown(C4KeyCodeEx key);
bool KeyDown(const C4KeyCodeEx &key);
public:
KeySelDialog(int32_t iKeyID, int32_t iCtrlSet, bool fGamepad);
virtual ~KeySelDialog();

View File

@ -1421,10 +1421,10 @@ StdStrBuf PlrControlKeyName(int32_t iPlayer, int32_t iControl, bool fShort)
// player control
if (pPlr)
{
if (Inside<int32_t>(pPlr->Control,C4P_Control_Keyboard1,C4P_Control_Keyboard4))
return C4KeyCodeEx::KeyCode2String(Config.Controls.Keyboard[pPlr->Control][iControl], true, fShort);
if (Inside<int32_t>(pPlr->Control,C4P_Control_GamePad1,C4P_Control_GamePadMax))
return C4KeyCodeEx::KeyCode2String(Config.Gamepads[pPlr->Control-C4P_Control_GamePad1].Button[iControl], true, fShort);
if (Inside<int32_t>(pPlr->ControlSet,C4P_Control_Keyboard1,C4P_Control_Keyboard4))
return C4KeyCodeEx::KeyCode2String(Config.Controls.Keyboard[pPlr->ControlSet][iControl], true, fShort);
if (Inside<int32_t>(pPlr->ControlSet,C4P_Control_GamePad1,C4P_Control_GamePadMax))
return C4KeyCodeEx::KeyCode2String(Config.Gamepads[pPlr->ControlSet-C4P_Control_GamePad1].Button[iControl], true, fShort);
}
// global control
else
@ -1475,24 +1475,8 @@ void C4Viewport::DrawPlayerControls(C4TargetFacet &cgo)
ty = cgo.Y+15;
break;
}
int32_t iShowCtrl = ::Players.Get(Player)->ShowControl;
int32_t iLastCtrl = Com2Control(::Players.Get(Player)->LastCom);
int32_t scwdt=size/3,schgt=size/4;
bool showtext;
const int32_t C4MaxShowControl = 10;
for (int32_t iCtrl=0; iCtrl<C4MaxShowControl; iCtrl++)
if (iShowCtrl & (1<<iCtrl))
{
showtext= iShowCtrl & (1<<(iCtrl+C4MaxShowControl)) ;
if (iShowCtrl & (1<<(iCtrl+2*C4MaxShowControl)))
if (::Game.iTick35>18) showtext=false;
C4Facet ccgo;
ccgo.Set(cgo.Surface,tx+scwdt*(iCtrl%3),ty+schgt*(iCtrl/3),scwdt,schgt);
DrawControlKey(ccgo,iCtrl,(iLastCtrl==iCtrl) ? 1 : 0,
showtext ? PlrControlKeyName(Player,iCtrl,true).getData() : NULL);
}
// TODO
}
extern int32_t DrawMessageOffset;
@ -1510,20 +1494,20 @@ void C4Viewport::DrawPlayerStartup(C4TargetFacet &cgo)
cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2+55,
cgo.Y+cgo.Hgt * 2/3 - 10 + DrawMessageOffset,
0,0);
if (Inside<int32_t>(pPlr->Control,C4P_Control_Keyboard1,C4P_Control_Keyboard4))
if (Inside<int32_t>(pPlr->ControlSet,C4P_Control_Keyboard1,C4P_Control_Keyboard4))
{
GfxR->fctKeyboard.Draw(cgo.Surface,
cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2,
cgo.Y+cgo.Hgt * 2/3 + DrawMessageOffset,
pPlr->Control-C4P_Control_Keyboard1,0);
pPlr->ControlSet-C4P_Control_Keyboard1,0);
iNameHgtOff=GfxR->fctKeyboard.Hgt;
}
else if (Inside<int32_t>(pPlr->Control,C4P_Control_GamePad1,C4P_Control_GamePad4))
else if (Inside<int32_t>(pPlr->ControlSet,C4P_Control_GamePad1,C4P_Control_GamePad4))
{
GfxR->fctGamepad.Draw(cgo.Surface,
cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2,
cgo.Y+cgo.Hgt * 2/3 + DrawMessageOffset,
pPlr->Control-C4P_Control_GamePad1,0);
pPlr->ControlSet-C4P_Control_GamePad1,0);
iNameHgtOff=GfxR->fctGamepad.Hgt;
}

View File

@ -521,7 +521,7 @@ struct StdSTLContainerAdapt
// Write all entries
for (typename C::const_iterator i = rStruct.begin(); i != rStruct.end(); ++i)
{
if (i != rStruct.begin()) pComp->Seperator(eSep);
if (i != rStruct.begin() && eSep) pComp->Seperator(eSep);
pComp->Value(const_cast<T &>(*i));
}
}
@ -552,7 +552,7 @@ struct StdSTLContainerAdapt
break;
}
}
while(pComp->Seperator(eSep));
while(!eSep || pComp->Seperator(eSep));
}
}
// Operators for default checking/setting

View File

@ -110,6 +110,7 @@ public:
// ending the naming, too.
enum Sep
{
SEP_NONE=0, // No seperator ("")
SEP_SEP, // Array seperation (",")
SEP_SEP2, // Array seperation 2 (";")
SEP_SET, // Map pair seperation ("=")

View File

@ -766,7 +766,7 @@ C4GameControlPacket *C4GameControlNetwork::PackCompleteCtrl(int32_t iTick)
{
// async mode: wait n extra frames for slow clients
const int iMaxWait = (Config.Network.AsyncMaxWait * 1000) / iTargetFPS;
if(eMode != CNM_Async || iWaitStart == -1 || timeGetTime() <= iWaitStart + iMaxWait)
if(eMode != CNM_Async || iWaitStart == -1 || timeGetTime() <= uint32_t(iWaitStart + iMaxWait))
return false;
}

View File

@ -119,7 +119,7 @@ const C4PktHandlingData PktHandlingData[] =
{ CID_JoinPlr, PC_Control, "Join Player", false, true, 0, PKT_UNPACK(C4ControlJoinPlayer) },
{ CID_RemovePlr, PC_Control, "Remove Player", false, true, 0, PKT_UNPACK(C4ControlRemovePlr) },
{ CID_PlrSelect, PC_Control, "Player Select", false, true, 0, PKT_UNPACK(C4ControlPlayerSelect)},
{ CID_PlrControl, PC_Control, "Player Control", false, true, 0, PKT_UNPACK(C4ControlPlayerControl)},
{ CID_PlrControl, PC_Control, "Player Control", false, true, 0, PKT_UNPACK(C4ControlPlayerControl)},
{ CID_PlrCommand, PC_Control, "Player Command", false, true, 0, PKT_UNPACK(C4ControlPlayerCommand)},
{ CID_Message, PC_Control, "Message", false, true, 0, PKT_UNPACK(C4ControlMessage) },
{ CID_EMMoveObj, PC_Control, "EM Move Obj", false, true, 0, PKT_UNPACK(C4ControlEMMoveObject)},

View File

@ -162,6 +162,7 @@ enum C4PacketType
CID_PlrControl = CID_First | 0x21,
CID_PlrCommand = CID_First | 0x22,
CID_Message = CID_First | 0x23,
CID_PlrControl2 = CID_First | 0x24,
CID_EMMoveObj = CID_First | 0x30,
CID_EMDrawTool = CID_First | 0x31,

View File

@ -379,6 +379,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

View File

@ -80,6 +80,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

@ -386,7 +386,7 @@ void CStdGL::BlitLandscape(SURFACE sfcSource, float fx, float fy,
glActiveTexture(GL_TEXTURE0);
}
DWORD dwModMask = 0;
SetupTextureEnv(false, mattextures);
SetupTextureEnv(false, !!mattextures);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);

View File

@ -378,9 +378,9 @@ bool RestoreWindowPosition(HWND hwnd,
if (!GetRegistryString(szSubKey,szWindowName,regstr,100))
return false;
if (SEqual(regstr,"Maximized"))
return ShowWindow(hwnd,SW_MAXIMIZE | SW_NORMAL);
return !!ShowWindow(hwnd,SW_MAXIMIZE | SW_NORMAL);
if (SEqual(regstr,"Minimized"))
return ShowWindow(hwnd,SW_MINIMIZE | SW_NORMAL);
return !!ShowWindow(hwnd,SW_MINIMIZE | SW_NORMAL);
SCopySegment(regstr,0,buffer2,',',4); sscanf(buffer2,"%i",&x);
SCopySegment(regstr,1,buffer2,',',4); sscanf(buffer2,"%i",&y);
if (SCopySegment(regstr,2,buffer2,',',4)) sscanf(buffer2,"%i",&wdt); else fSetSize=false;
@ -395,9 +395,9 @@ bool RestoreWindowPosition(HWND hwnd,
return false;
// Hide window
if (fHidden)
return ShowWindow(hwnd, SW_HIDE);
return !!ShowWindow(hwnd, SW_HIDE);
// Show window
return ShowWindow(hwnd, SW_NORMAL);
return !!ShowWindow(hwnd, SW_NORMAL);
}
//------------------------------ Registry compiler ------------------------------------------

View File

@ -275,7 +275,15 @@ void C4AulScriptEngine::Link(C4DefList *rDefs)
Action->SetProperty(Strings.P[P_Directions], C4VInt(1));
Action->SetProperty(Strings.P[P_Step], C4VInt(1));
Action->SetProperty(Strings.P[P_Procedure], C4VInt(DFA_NONE));
GlobalNamed.GetItem("Action")->SetPropList(Action);
C4Value *pActionValDef = GlobalNamed.GetItem("Action");
if (!pActionValDef)
{
Log("WARNING: static Action not defined. Wrong System.c4g?");
}
else
{
pActionValDef->SetPropList(Action);
}
rDefs->CallEveryDefinition();
// display state

View File

@ -259,6 +259,7 @@
5862E21C1032E57A002CAE9E /* gzio.c in Sources */ = {isa = PBXBuildFile; fileRef = 5862E1171032E57A002CAE9E /* gzio.c */; };
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
D0AD7B7F0FCD7C1C00EAC39A /* C4PlayerControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0AD7B7E0FCD7C1C00EAC39A /* C4PlayerControl.cpp */; };
D421AB4E0B8F50C000CAF2D6 /* SDL_mixer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D421AB4D0B8F50C000CAF2D6 /* SDL_mixer.framework */; };
D421AB900B8F59D500CAF2D6 /* SDL_mixer.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D421AB4D0B8F50C000CAF2D6 /* SDL_mixer.framework */; };
D444F04C0B3EB6F8001B60F7 /* SDLMain.nib in Resources */ = {isa = PBXBuildFile; fileRef = D444F04A0B3EB6F8001B60F7 /* SDLMain.nib */; };
@ -763,6 +764,8 @@
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8D1107320486CEB800E47090 /* Clonk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Clonk.app; sourceTree = BUILT_PRODUCTS_DIR; };
D40626910BB9418400815E5D /* c4group.mac */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = c4group.mac; sourceTree = BUILT_PRODUCTS_DIR; };
D0AD7B7D0FCD7C0C00EAC39A /* C4PlayerControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C4PlayerControl.h; sourceTree = "<group>"; };
D0AD7B7E0FCD7C1C00EAC39A /* C4PlayerControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4PlayerControl.cpp; sourceTree = "<group>"; };
D421AB4D0B8F50C000CAF2D6 /* SDL_mixer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL_mixer.framework; sourceTree = "<group>"; };
D439C8EF0B8CB0F6001D508B /* gzio.c */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.c.c; name = gzio.c; path = ../standard/zlib/gzio.c; sourceTree = SOURCE_ROOT; };
D439C8FE0B8CB0F6001D508B /* zutil.h */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.c.h; name = zutil.h; path = ../standard/zlib/zutil.h; sourceTree = SOURCE_ROOT; };
@ -1550,6 +1553,7 @@
name = zlib;
sourceTree = "<group>";
};
D0AD7B7D0FCD7C0C00EAC39A /* C4PlayerControl.h */,
F4ED29E70B441665003F4D0A /* planet */ = {
isa = PBXGroup;
children = (
@ -1731,6 +1735,7 @@
5862E1281032E57A002CAE9E /* C4WinMain.cpp in Sources */,
5862E1291032E57A002CAE9E /* C4Config.cpp in Sources */,
5862E12A1032E57A002CAE9E /* C4ConfigShareware.cpp in Sources */,
D0AD7B7F0FCD7C1C00EAC39A /* C4PlayerControl.cpp in Sources */,
5862E12B1032E57A002CAE9E /* C4SecurityCertificates.cpp in Sources */,
5862E12C1032E57A002CAE9E /* C4Control.cpp in Sources */,
5862E12D1032E57A002CAE9E /* C4GameControl.cpp in Sources */,