forked from Mirrors/openclonk
merge
commit
f584896446
|
@ -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
|
||||
|
|
|
@ -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 && !release)
|
||||
{
|
||||
// Sprungtaste gedrueckt. Der vom Spieler ausgewaehlte Clonk soll springen
|
||||
var player_clonk = GetCursor(player);
|
||||
if (player_clonk && 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 && 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>
|
|
@ -5,9 +5,6 @@
|
|||
// Zauberei - ben<65>tigt, wenn der Clonk Zielzauber z.B. aus dem Zauberturm zaubert
|
||||
// Auch ben<65>tigt f<>r den K<>nig
|
||||
local pAimer; // Aktive Zielsteuerung; wird abgrbrochen, wenn der Zauberer gest<73>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
|
||||
|
||||
local pInventory;
|
||||
|
||||
/* Initialisierung */
|
||||
|
@ -73,82 +70,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 <20>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 <20>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 <20>berladene Steuerung
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected func ControlRightReleased()
|
||||
{
|
||||
// Steuerung an Pferd weiterleiten
|
||||
if (IsRiding()) return GetActionTarget()->~ControlRightReleased(this);
|
||||
// Keine <20>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 <20>berladene Steuerung
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected func ControlUpReleased()
|
||||
{
|
||||
// Steuerung an Pferd weiterleiten
|
||||
if (IsRiding()) return GetActionTarget()->~ControlUpReleased(this);
|
||||
// Keine <20>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()
|
||||
|
@ -169,105 +162,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 <20>berladene Steuerung
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected func ControlDownReleased()
|
||||
{
|
||||
// Steuerung an Pferd weiterleiten
|
||||
if (IsRiding()) return GetActionTarget()->~ControlDownReleased(this);
|
||||
// Keine <20>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 <20>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 <20>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 <20>berladene Steuerung
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected func ControlDigReleased()
|
||||
{
|
||||
// Steuerung an Pferd weiterleiten
|
||||
if (IsRiding()) return GetActionTarget()->~ControlDigReleased(this);
|
||||
// Keine <20>berladene Steuerung
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected func ControlDigSingle()
|
||||
{
|
||||
// Steuerung an Pferd weiterleiten
|
||||
if (IsRiding()) return GetActionTarget()->~ControlDigSingle(this);
|
||||
// Keine <20>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 <20>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 <20>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 <20>berladene Steuerung
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data)
|
||||
{
|
||||
// Kommando MoveTo an Pferd weiterleiten
|
||||
|
@ -292,6 +186,9 @@ protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
public func ControlDownDouble() {} // dummy
|
||||
|
||||
|
||||
/* Verwandlung */
|
||||
|
||||
private func RedefinePhysical(szPhys, idTo)
|
||||
|
@ -401,6 +298,7 @@ public func Redefine(idTo)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Essen */
|
||||
|
||||
public func Feed(iLevel)
|
||||
|
@ -410,6 +308,7 @@ public func Feed(iLevel)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Aktionen */
|
||||
|
||||
private func Riding()
|
||||
|
@ -502,6 +401,7 @@ protected func Scaling()
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Ereignisse */
|
||||
|
||||
protected func CatchBlow()
|
||||
|
@ -543,9 +443,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
|
||||
|
@ -584,6 +481,7 @@ protected func CheckStuck()
|
|||
SetPosition(GetX(), GetY() + 1);
|
||||
}
|
||||
|
||||
|
||||
/* Status */
|
||||
|
||||
public func IsRiding()
|
||||
|
@ -594,6 +492,7 @@ public func IsRiding()
|
|||
|
||||
public func IsClonk() { return 1; }
|
||||
|
||||
|
||||
/* Kontext */
|
||||
|
||||
public func ContextRelease(pCaller)
|
||||
|
@ -645,6 +544,7 @@ public func ContextHome(pCaller)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Hilfsfunktion */
|
||||
|
||||
public func ContainedCall(string strFunction, object pTarget)
|
||||
|
@ -654,34 +554,6 @@ public func ContainedCall(string strFunction, object pTarget)
|
|||
AddCommand(this, "Enter", pTarget);
|
||||
}
|
||||
|
||||
/* Steuerung */
|
||||
|
||||
protected func ControlSpecial2()
|
||||
{
|
||||
[$CtrlMenuDesc$|Image=CXTX]
|
||||
// In einem Geb<65>ude oder Fahrzeug: das Kontextmen<65> des Geb<65>udes <20>ffnen
|
||||
if (Contained())
|
||||
if ((Contained()->GetCategory() & C4D_Structure) || (Contained()->GetCategory() & C4D_Vehicle))
|
||||
{
|
||||
SetCommand("Context",nil,0,0,Contained());
|
||||
return ExecuteCommand();
|
||||
}
|
||||
// Fasst ein Objekt an: Kontextmen<65> des angefassten Objekts <20>ffnen
|
||||
if (GetAction() == "Push")
|
||||
{
|
||||
SetCommand("Context",nil,0,0,GetActionTarget());
|
||||
return ExecuteCommand();
|
||||
}
|
||||
// Tr<54>gt ein Objekt: Kontextmen<65> des ersten getragenen Objekts <20>ffnen
|
||||
if (Contents(0))
|
||||
{
|
||||
SetCommand("Context",nil,0,0,Contents(0));
|
||||
return ExecuteCommand();
|
||||
}
|
||||
// Ansonsten das Kontextmen<65> des Clonks <20>ffnen
|
||||
SetCommand("Context",nil,0,0,this);
|
||||
return ExecuteCommand();
|
||||
}
|
||||
|
||||
/* Callback beim Auswahl aus dem Construct-Kontextmenu */
|
||||
|
||||
|
@ -693,6 +565,7 @@ public func ControlCommandConstruction(target, x, y, target2, def)
|
|||
return FinishCommand(false, 0) ;
|
||||
}
|
||||
|
||||
|
||||
/* Automatische Produktion */
|
||||
|
||||
public func ControlCommandAcquire(target, x, y, target2, def)
|
||||
|
@ -752,6 +625,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)
|
||||
|
@ -814,6 +688,7 @@ private func UpdateInventorySelection()
|
|||
// if(iSave == 10) Log("ERROR: Inventory doesn't match");
|
||||
}
|
||||
|
||||
|
||||
/* Einsammeln */
|
||||
|
||||
public func RejectCollect(id idObject, object pObject)
|
||||
|
@ -840,6 +715,7 @@ public func RejectCollect(id idObject, object pObject)
|
|||
return !pInventory->FreeSpace(pObject);//GetNonSpecialCount()>=MaxContentsCount();
|
||||
}
|
||||
|
||||
|
||||
/* Itemlimit */
|
||||
public func MaxContentsCount() { return 3; }
|
||||
|
||||
|
@ -936,24 +812,6 @@ public func DescendVehicle()
|
|||
}
|
||||
|
||||
|
||||
/* Effektsteuerung */
|
||||
|
||||
private func Control2Effect(string szControl)
|
||||
{
|
||||
// Von Effektzahl abw<62>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 */
|
||||
|
||||
|
@ -1025,40 +883,11 @@ private func GetArrowCount()
|
|||
}
|
||||
|
||||
|
||||
/* Zauberei - ben<65>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<75>ck gegeben werden
|
||||
return (pSpellOrigin->~SpellFailed(idSpell, this));
|
||||
// Magieenergie zur<75>ckgeben
|
||||
DoMagicEnergy(idSpell->GetDefValue(), true);
|
||||
// Alchemische Zutaten zur<75>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<69>ten daf<61>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 = {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
|
|
138
src/C4Game.cpp
138
src/C4Game.cpp
|
@ -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?)
|
||||
|
@ -1873,9 +1876,15 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp)
|
|||
|
||||
pComp->Value(mkNamingAdapt(mkNamingPtrAdapt(pGlobalEffects, "GlobalEffects"), "Effects"));
|
||||
|
||||
// scoreboard compiles into main level [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());
|
||||
|
@ -1884,7 +1893,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp)
|
|||
// 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2280,6 +2289,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);
|
||||
|
@ -2456,9 +2468,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();
|
||||
|
@ -3174,37 +3183,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();
|
||||
|
||||
|
@ -3251,12 +3229,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);
|
||||
|
@ -3288,71 +3289,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;
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "C4Scoreboard.h"
|
||||
#include <C4VideoPlayback.h>
|
||||
#include <C4ScriptHost.h>
|
||||
|
||||
#include <C4PlayerControl.h>
|
||||
class C4ObjectInfo;
|
||||
|
||||
class C4Game
|
||||
|
@ -90,6 +90,8 @@ class C4Game
|
|||
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;
|
||||
|
@ -160,9 +162,6 @@ class C4Game
|
|||
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);
|
||||
bool Init();
|
||||
bool PreInit();
|
||||
|
@ -249,6 +248,8 @@ class C4Game
|
|||
bool SpeedUp();
|
||||
bool SlowDown();
|
||||
bool InitKeyboard(); // register main keyboard input functions
|
||||
void UpdateLanguage();
|
||||
bool InitPlayerControlSettings();
|
||||
|
||||
protected:
|
||||
bool InitSystem();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (!Inside<int>(iCom, COM_ReleaseFirst, COM_ReleaseLast))
|
||||
pPlr->CountControl(C4Player::PCID_DirectCom, iCom*10000+iData);
|
||||
pPlr->InCom(iCom, iData);
|
||||
// neutral control packet: Execute in global control
|
||||
}
|
||||
else
|
||||
{
|
||||
// player-based control: Execute on control owned by player
|
||||
C4Player *pPlr=::Players.Get(iPlr);
|
||||
if (pPlr)
|
||||
{
|
||||
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));
|
||||
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,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "C4PacketBase.h"
|
||||
#include "C4PlayerInfo.h"
|
||||
#include "C4Client.h"
|
||||
#include "C4KeyboardInput.h"
|
||||
#include "C4ObjectList.h"
|
||||
|
||||
class C4Record;
|
||||
|
@ -169,14 +170,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
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -2777,7 +2777,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)] = 0xff;
|
||||
Surface8->pPal->Alpha[MatTex2PixCol(tex)+IFT] = 0xff;
|
||||
|
|
|
@ -353,6 +353,7 @@ void C4GameObjects::PutSolidMasks()
|
|||
|
||||
void C4GameObjects::DeleteObjects(bool fDeleteInactive)
|
||||
{
|
||||
Sectors.ClearObjects();
|
||||
C4ObjectList::DeleteObjects();
|
||||
BackObjects.Clear();
|
||||
ForeObjects.Clear();
|
||||
|
|
|
@ -3058,421 +3058,7 @@ 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());
|
||||
// objects may overload hardcoded actions
|
||||
C4Def *pCDef = Contained->Def;
|
||||
bool result = false;
|
||||
C4Player * pPlr = ::Players.Get(Controller);
|
||||
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_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:
|
||||
{
|
||||
// Call object control first in case it overloads
|
||||
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;
|
||||
}
|
||||
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:
|
||||
{
|
||||
// Call object control first in case it overloads
|
||||
if(Action.Target)
|
||||
if (Action.Target->CallControl(pPlayer, byCom, &C4AulParSet(C4VObj(this))))
|
||||
return;
|
||||
// Clonk direct control
|
||||
switch (byCom)
|
||||
{
|
||||
case COM_Down: case COM_Down_D: ObjectComUnGrab(this); break;
|
||||
case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break;
|
||||
case COM_Up:
|
||||
// Target -> enter
|
||||
if (ObjectComEnter(Action.Target))
|
||||
{
|
||||
ObjectComMovement(this,COMD_Stop);
|
||||
break;
|
||||
}
|
||||
// Else, comdir up for target straightening
|
||||
default:
|
||||
AutoStopUpdateComDir();
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -216,7 +216,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);
|
||||
|
||||
|
||||
|
@ -334,9 +333,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 SetOwner(int32_t iOwner);
|
||||
bool SetPlrViewRange(int32_t iToRange);
|
||||
void SetOnFire(bool OnFire) { this->OnFire = OnFire; SetOCF(); }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -964,8 +964,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
|
||||
|
@ -976,7 +976,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
|
||||
|
|
|
@ -347,20 +347,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()
|
||||
|
|
|
@ -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,12 @@ bool C4LSectors::CheckSort()
|
|||
return true;
|
||||
}
|
||||
|
||||
void C4LSectors::ClearObjects()
|
||||
{
|
||||
for (int cnt=0; cnt<Size; cnt++) Sectors[cnt].ClearObjects();
|
||||
SectorOut.ClearObjects();
|
||||
}
|
||||
|
||||
/* landscape area */
|
||||
|
||||
bool C4LArea::operator == (const C4LArea &Area) const
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1015,7 +1015,6 @@ void C4Player::Default()
|
|||
ControlCount = ActionCount = 0;
|
||||
LastControlType = PCID_None;
|
||||
LastControlID = 0;
|
||||
PressedComs = 0;
|
||||
pMsgBoardQuery = NULL;
|
||||
pGamepad = NULL;
|
||||
NoEliminationCheck = false;
|
||||
|
@ -1143,26 +1142,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()
|
||||
|
@ -1309,30 +1289,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
|
||||
|
@ -1389,72 +1345,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);
|
||||
|
||||
|
@ -1468,7 +1359,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));
|
||||
|
@ -1497,10 +1388,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));
|
||||
|
@ -1512,6 +1399,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)
|
||||
|
@ -1522,9 +1412,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;
|
||||
|
@ -1606,7 +1497,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;
|
||||
|
@ -1628,9 +1519,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;
|
||||
|
@ -1770,7 +1658,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
|
||||
|
@ -1782,32 +1670,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;
|
||||
|
|
|
@ -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,7 @@ 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);
|
||||
|
@ -190,7 +186,7 @@ class C4Player: public C4PlayerInfoCore
|
|||
bool ObjectInCrew(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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -762,6 +762,7 @@ static bool FnSetActionData(C4AulObjectContext *cthr, long iData)
|
|||
}
|
||||
|
||||
static C4Void FnSetComDir(C4AulObjectContext *cthr, long ncomdir)
|
||||
|
||||
{
|
||||
cthr->Obj->Action.ComDir=ncomdir;
|
||||
return C4VNull;
|
||||
|
@ -2427,24 +2428,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);
|
||||
|
@ -3668,7 +3651,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)
|
||||
|
@ -5431,6 +5414,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
|
||||
|
@ -5611,6 +5620,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
|
|||
AddFunc(pEngine, "Bubble", FnBubble);
|
||||
AddFunc(pEngine, "SetAction", FnSetAction);
|
||||
AddFunc(pEngine, "SetActionData", FnSetActionData);
|
||||
|
||||
AddFunc(pEngine, "SetBridgeActionData", FnSetBridgeActionData);
|
||||
AddFunc(pEngine, "GetAction", FnGetAction);
|
||||
AddFunc(pEngine, "GetActTime", FnGetActTime);
|
||||
|
@ -5691,8 +5701,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);
|
||||
|
@ -5900,6 +5908,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);
|
||||
|
|
|
@ -155,6 +155,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
|
||||
|
|
|
@ -1171,7 +1171,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; }
|
||||
|
@ -1679,7 +1679,7 @@ namespace C4GUI {
|
|||
bool KeyBack();
|
||||
bool KeyAbort();
|
||||
bool KeyConfirm();
|
||||
bool KeyHotkey(C4KeyCodeEx key);
|
||||
bool KeyHotkey(const C4KeyCodeEx &key);
|
||||
|
||||
private:
|
||||
static int32_t iGlobalMenuIndex;
|
||||
|
@ -2013,7 +2013,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:
|
||||
|
@ -2337,7 +2337,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) {}
|
||||
|
@ -2374,7 +2374,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) {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -166,12 +166,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 +194,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 +219,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 +242,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 +280,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 +302,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 +310,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 +361,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 +387,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 +407,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 +433,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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ("=")
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; };
|
||||
|
@ -1024,6 +1027,7 @@
|
|||
5862DF801032E57A002CAE9E /* game */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0AD7B7E0FCD7C1C00EAC39A /* C4PlayerControl.cpp */,
|
||||
5862DF811032E57A002CAE9E /* C4Constants.h */,
|
||||
5862DF821032E57A002CAE9E /* C4GameVersion.h */,
|
||||
5862DF831032E57A002CAE9E /* C4Physics.h */,
|
||||
|
@ -1550,6 +1554,7 @@
|
|||
name = zlib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D0AD7B7D0FCD7C0C00EAC39A /* C4PlayerControl.h */,
|
||||
F4ED29E70B441665003F4D0A /* planet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1732,6 +1737,7 @@
|
|||
5862E1291032E57A002CAE9E /* C4Config.cpp in Sources */,
|
||||
5862E12A1032E57A002CAE9E /* C4ConfigShareware.cpp in Sources */,
|
||||
5862E12B1032E57A002CAE9E /* C4SecurityCertificates.cpp in Sources */,
|
||||
D0AD7B7F0FCD7C1C00EAC39A /* C4PlayerControl.cpp in Sources */,
|
||||
5862E12C1032E57A002CAE9E /* C4Control.cpp in Sources */,
|
||||
5862E12D1032E57A002CAE9E /* C4GameControl.cpp in Sources */,
|
||||
5862E12E1032E57A002CAE9E /* C4GameParameters.cpp in Sources */,
|
||||
|
|
Loading…
Reference in New Issue