stable-5.2
Tobias Zwick 2009-10-11 22:00:34 +02:00
commit cbff400fe7
63 changed files with 3225 additions and 1107 deletions

View File

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

View File

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

View File

@ -4,7 +4,6 @@
<funcs>
<func>
<title>Call</title>
<deprecated />
<category>Script</category><subcat>Funktionsaufruf</subcat>
<version>4.6.5.0 CP</version>
<syntax>
@ -14,7 +13,7 @@
<param><type>any</type><name>...</name><desc>Parameter der Funktion.</desc></param>
</params>
</syntax>
<desc>Ruft die lokale Funktion szFunction auf. Stattdessen sollte dieselbe Syntax wie bei engineinternen Funktionen genutzt werden.</desc>
<desc>Ruft die lokale Funktion szFunction auf. Wird "~" vor den Funktionsnamen gesetzt, wird der Aufruf failsafe durchgeführt.</desc>
<related>
<funclink>ProtectedCall</funclink>
<funclink>PrivateCall</funclink>

View File

@ -14,7 +14,7 @@
<param><type>int</type><name>iIndex</name><desc>Index des Clonks in der Crew</desc></param>
</params>
</syntax>
<desc>Gibt ein Mannschaftsmitglied eines Spielers zurück.</desc>
<desc>Gibt ein Mannschaftsmitglied eines Spielers zurück. Wird ein ungültiger Index angegeben, gibt die Funktion nil zurück.</desc>
<examples>
<example>
<code>var i=<funclink>GetCrewCount</funclink>(0); <funclink>while</funclink> (i--) <funclink>Kill</funclink>(GetCrew(0, i));</code>

View File

@ -15,7 +15,7 @@
<param><type>bool</type><name>fNoSelectArrow</name><desc>Wenn ungleich 0, wird bei der Anwahl kein Auswahlpfeil angezeigt. Ab CE.</desc><optional /></param>
</params>
</syntax>
<desc>Setzt die Auswahlmarkierung eines Spielers auf ein Zielobjekt und aktiviert dieses für die Steuerung.</desc>
<desc>Setzt die Auswahlmarkierung eines Spielers auf ein Zielobjekt und aktiviert dieses für die Steuerung. Gibt false zurück falls das Zielobjekt momentan durch <funclink>SetCrewEnabled</funclink> deaktiviert ist.</desc>
<related>
<funclink>GetCursor</funclink>
<funclink>SelectCrew</funclink>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,6 +1,7 @@
/*-- Der Clonk --*/
#strict 2
#include HUDS
// Context menu
#include L_CM;
@ -45,91 +46,19 @@ protected func Swimming2()
protected func Recruitment(int iPlr) {
// Broadcast f<>r Crew
GameCallEx("OnClonkRecruitment", this, iPlr);
_inherited(...);
}
/* Steuerung */
protected func ControlLeft()
{
// 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()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlLeftDouble")) return true;
}
protected func ControlRightDouble()
{
// Steuerung an Effekt weitergeben
if (Control2Effect("ControlRightDouble")) 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();
}
private func DolphinJump()
{
// nur wenn an Meeresoberfl<66>che
if(!InLiquid()) return 0;
if(GBackSemiSolid(0,-1)) return 0;
if(!InLiquid()) return false;
if(GBackSemiSolid(0,-1)) return false;
// Nicht wenn deaktiviert (z.B. Ohnmacht)
if (GetID()->GetActMapVal("ObjectDisabled", GetAction())) return false;
// herausspringen
@ -141,106 +70,36 @@ private func DolphinJump()
if(SimFlight(iX,iY,iXDir,iYDir,25,50))
if(GBackLiquid(iX-GetX(),iY-GetY()) && GBackLiquid(iX-GetX(),iY+9-GetY()))
SetAction("Dive");
return true;
}
protected func ControlDown()
protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data)
{
// 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))
// Kommando MoveTo an Pferd weiterleiten
if (szCommand == "MoveTo")
if (IsRiding())
return GetActionTarget()->~ControlCommand(szCommand, pTarget, iTx, iTy);
// Anderes Kommando beim Reiten: absteigen (Ausnahme: Context)
if (IsRiding() && szCommand != "Context")
{
GetActionTarget()->SetComDir(COMD_Stop);
GetActionTarget()->~ControlDownDouble(this);
}
// RejectConstruction Callback beim Bauen durch Drag'n'Drop aus einem Gebaeude-Menu
if(szCommand == "Construct")
{
if(Data->~RejectConstruction(iTx - GetX(), iTy - GetY(), this) )
{
SetAction("RideThrow");
return 1;
}
// Keine <20>berladene Steuerung
}
// Kein <20>berladenes Kommando
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;
}
public func ControlDownDouble() {} // dummy
/* Verwandlung */
@ -351,6 +210,7 @@ public func Redefine(idTo)
return 1;
}
/* Essen */
public func Feed(iLevel)
@ -360,6 +220,7 @@ public func Feed(iLevel)
return 1;
}
/* Aktionen */
private func Riding()
@ -452,6 +313,7 @@ protected func Scaling()
return 1;
}
/* Ereignisse */
protected func CatchBlow()
@ -493,9 +355,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
@ -534,10 +393,13 @@ protected func CheckStuck()
SetPosition(GetX(), GetY() + 1);
}
/* Status */
public func IsClonk() { return 1; }
/* Hilfsfunktion */
public func ContainedCall(string strFunction, object pTarget)
@ -547,6 +409,8 @@ public func ContainedCall(string strFunction, object pTarget)
AddCommand(this, "Enter", pTarget);
}
/* Trinken */
public func Drink(object pDrink)
@ -609,6 +473,7 @@ private func UpdateInventorySelection()
// if(iSave == 10) Log("ERROR: Inventory doesn't match");
}
/* Einsammeln */
public func RejectCollect(id idObject, object pObject)
@ -635,6 +500,7 @@ public func RejectCollect(id idObject, object pObject)
return !pInventory->FreeSpace(pObject);//GetNonSpecialCount()>=MaxContentsCount();
}
/* Itemlimit */
public func MaxContentsCount() { return 3; }
@ -705,24 +571,6 @@ private func GetNonSpecialCount()
return iCnt;
}
/* 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 */
@ -794,40 +642,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 = {

View File

@ -29,8 +29,8 @@ public func SetBarOffset(int x, int y, int num)
public func RemoveBarLayers(int la)
{
// remove layers
SetGraphics(nil,nil,nil,la);
SetGraphics(nil,nil,nil,la+1);
SetGraphics(nil,nil,la);
SetGraphics(nil,nil,la+1);
}
public func SetBarLayers(int la, int num)

View File

@ -13,6 +13,10 @@
*/
/*
TODO: Graphical feedback for disabled crew members (grey?)
*/
local crew, breathbar, magicbar, hotkey;
public func BarSpacing() { return -5; }
@ -72,7 +76,8 @@ public func MouseSelection(int plr)
{
if(!crew) return false;
if(plr != GetOwner()) return false;
if(!(crew->GetCrewEnabled())) false;
// select this clonk
crew->SetCursor(plr);
@ -99,7 +104,7 @@ public func SetHotkey(int num)
{
if(num < 0 || num > 9)
{
SetGraphics(nil,nil,nil,12);
SetGraphics(nil,nil,12);
hotkey = false;
return;
}
@ -146,7 +151,7 @@ public func UpdateRank()
var ranky = -15000;
SetGraphics(nil,RANK,10,GFXOV_MODE_Action,Format("Rank%d",nrank));
SetObjDrawTransform(1000,0,rankx,0,1000,ranky, nil, 10);
SetObjDrawTransform(1000,0,rankx,0,1000,ranky, 10);
// extra rank (the star if the clonk is too experienced for normal ranks)
if(brank > 0)
@ -246,7 +251,7 @@ private func AddBreathBar()
SetBarLayers(4,1);
SetBarOffset(0,BarOffset(1),1);
SetBarDimensions(GetDefWidth(),BreathBarHeight(),1);
SetClrModulation(RGB(0,200,200),nil,5);
SetClrModulation(RGB(0,200,200),5);
breathbar = true;

View File

@ -1,5 +1,3 @@
#strict 2
#appendto CLNK
local selector;
@ -23,6 +21,5 @@ protected func Recruitment(int iPlr)
{
if(!FindObject(Find_ID(HUDC),Find_Owner(iPlr)))
CreateObject(HUDC,10,10,iPlr);
return _inherited(...);
}

View File

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

View File

@ -0,0 +1,663 @@
#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 player
if (Control2Player(plr,ctrl, x, y, strength, repeat, release)) return true;
// 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;
}
// Control2Player
// Player-wide controls
global func Control2Player(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
{
// select previous or next
if (ctrl == CON_PreviousCrew)
return ShiftCursor(plr, true);
if (ctrl == CON_NextCrew)
return ShiftCursor(plr, false);
// all those hotkeys...
var hotkey = 0;
if (ctrl == CON_PlayerHotkey0) hotkey = 10;
if (ctrl == CON_PlayerHotkey1) hotkey = 1;
if (ctrl == CON_PlayerHotkey2) hotkey = 2;
if (ctrl == CON_PlayerHotkey3) hotkey = 3;
if (ctrl == CON_PlayerHotkey4) hotkey = 4;
if (ctrl == CON_PlayerHotkey5) hotkey = 5;
if (ctrl == CON_PlayerHotkey6) hotkey = 6;
if (ctrl == CON_PlayerHotkey7) hotkey = 7;
if (ctrl == CON_PlayerHotkey8) hotkey = 8;
if (ctrl == CON_PlayerHotkey9) hotkey = 9;
if (hotkey > 0)
{
// valid crew number?
var crew = GetCrew(plr,hotkey-1);
if (!crew) return false;
// stop previously selected crew
StopSelected();
// set cursor if not disabled etc.
UnselectCrew(plr);
return SetCursor(plr,crew);
}
// select the complete crew
if (ctrl == CON_AllCrew)
{
for(var i = 0; i < GetCrewCount(plr); ++i)
{
var crew = GetCrew(plr,i);
SelectCrew(plr,crew, true);
}
StopSelected(plr);
}
return false;
}
global func StopSelected(int plr)
{
var cursor;
for(var i = 0; cursor = GetCursor(plr,i); ++i)
{
cursor->SetCommand("None");
cursor->SetComDir(COMD_Stop);
}
}
/* 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)
{
if (!this) return false;
// 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;
}
// Control redirected to script
global func Control2Script(int ctrl, int x, int y, int strength, bool repeat, bool release, string control, object obj)
{
// for the use command
if (ctrl == CON_Use)
{
var handled = false;
if(!release && !repeat)
handled = obj->Call(Format("~%sUse",control),this,x,y);
else if(release)
handled = obj->Call(Format("~%sUseStop",control),this,x,y);
else
handled = obj->Call(Format("~%sUseHolding",control),this,x,y);
return handled;
}
// overloads of movement commandos
else if (ctrl == CON_Left || ctrl == CON_Right || ctrl == CON_Down || ctrl == CON_Up)
{
if (release)
{
// if any movement key has been released, ControlStop is called
if (obj->Call(Format("~%sStop",control),this)) return true;
}
else
{
// Control*
if (ctrl == CON_Left) if (obj->Call(Format("~%sLeft",control),this)) return true;
if (ctrl == CON_Right) if (obj->Call(Format("~%sRight",control),this)) return true;
if (ctrl == CON_Up) if (obj->Call(Format("~%sUp",control),this)) return true;
if (ctrl == CON_Down) if (obj->Call(Format("~%sDown",control),this)) return true;
}
}
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)
{
if (!this) return false;
// Any control resets a previously given command
SetCommand("None");
// hotkeys (inventory, vehicle and structure control)
var hot = 0;
if (ctrl == CON_Hotkey0) hot = 10;
if (ctrl == CON_Hotkey1) hot = 1;
if (ctrl == CON_Hotkey2) hot = 2;
if (ctrl == CON_Hotkey3) hot = 3;
if (ctrl == CON_Hotkey4) hot = 4;
if (ctrl == CON_Hotkey5) hot = 5;
if (ctrl == CON_Hotkey6) hot = 6;
if (ctrl == CON_Hotkey7) hot = 7;
if (ctrl == CON_Hotkey8) hot = 8;
if (ctrl == CON_Hotkey9) hot = 9;
if (hot > 0) return this->~ControlHotkey(hot-1);
var proc = GetProcedure();
// building, vehicle, contents control
var house = Contained();
var vehicle = GetActionTarget();
var contents = nil;
// TODO
if (house)
{
if (Control2Script(ctrl, x, y, strength, repeat, release, "Contained", house))
return true;
}
else if (vehicle)
{
// control to grabbed vehicle or riding etc.
if (proc == "PUSH" || proc == "ATTACH")
if (Control2Script(ctrl, x, y, strength, repeat, release, "Control", vehicle))
return true;
}
else if (contents)
{
// out of convencience we call Control2Script, even though it can handle
// left, right, up and down, too. We don't want that, so this is why we
// check that ctrl is Use.
if (ctrl == CON_Use)
if (Control2Script(ctrl, x, y, strength, repeat, release, "Control", contents))
return true;
}
// clonk control
else
{
// for standard controls
if(Control2Script(ctrl, x, y, strength, repeat, release, "Control", this)) return true;
// and a few more...
if(ctrl == CON_Throw) if(this->~ControlThrow(this,x,y)) return true;
if(ctrl == CON_Jump) if(this->~ControlJump(this)) return true;
}
// everything down from here:
// standard controls that are called if not overloaded via script
// Movement controls
if (ctrl == CON_Left || ctrl == CON_Right || ctrl == CON_Up || ctrl == CON_Down || ctrl == CON_Jump)
return ObjectControlMovement(plr, ctrl, strength, release);
// Push controls
if (ctrl == CON_Grab || ctrl == CON_Ungrab || ctrl == CON_PushEnter || ctrl == CON_GrabPrevious || ctrl == CON_GrabNext)
return ObjectControlPush(plr, ctrl);
// Entrance controls
if (ctrl == CON_Enter || CON_Exit)
return ObjectControlEntrance(plr,ctrl);
// Inventory control
if (ctrl == CON_NextItem)
{
ShiftContents(false);
return true;
}
if (ctrl == CON_PreviousItem)
{
ShiftContents(true);
return true;
}
// only if not in house, not grabbing a vehicle and an item selected
if(!house && !vehicle && contents)
{
// throw
if (ctrl == CON_Throw)
{
if (proc == "SCALE" || proc == "HANGLE")
return PlayerObjectCommand(plr, false, "Drop");
else
return PlayerObjectCommand(plr, false, "Throw");
}
// drop
if (ctrl == CON_Drop)
{
return PlayerObjectCommand(plr, false, "Drop");
}
}
// Unhandled control
return false;
}
// ObjectControlEntrance
// Handles enter and exit
global func ObjectControlEntrance(int plr, int ctrl)
{
var proc = GetProcedure();
// enter
if (ctrl == CON_Enter)
{
// enter only if... one can
if (proc != "WALK" && proc != "SWIM" && proc != "SCALE" &&
proc != "HANGLE" && proc != "FLOAT" && proc != "FLIGHT") return false;
// a building with an entrance at right position is there?
var obj = GetEntranceObject();
if (!obj) return false;
PlayerObjectCommand(plr, false, "Enter", obj);
return true;
}
// exit
if (ctrl == CON_Exit)
{
if (!Contained()) return false;
PlayerObjectCommand(plr, false, "Exit");
return true;
}
return false;
}
// ObjectControlPush
// Handles push controls
global func ObjectControlPush(int plr, int ctrl)
{
if (!this) return false;
var proc = GetProcedure();
// grabbing
if (ctrl == CON_Grab)
{
// grab only if he walks
if (proc != "WALK") return false;
// only if there is someting to grab
var obj = FindObject(Find_OCF(OCF_Grab), Find_AtPoint(0,0), Find_Exclude(this));
if (!obj) return false;
// grab
PlayerObjectCommand(plr, false, "Grab", obj);
return true;
}
// grab next/previous
if (ctrl == CON_GrabNext)
return ShiftVehicle(plr, false);
if (ctrl == CON_GrabPrevious)
return ShiftVehicle(plr, true);
// ungrabbing
if (ctrl == CON_Ungrab)
{
// ungrab only if he pushes
if (proc != "PUSH") return false;
PlayerObjectCommand(plr, false, "Ungrab");
return true;
}
// push into building
if (ctrl == CON_PushEnter)
{
if (proc != "PUSH") return false;
// a building with an entrance at right position is there?
var obj = GetActionTarget()->GetEntranceObject();
if (!obj) return false;
PlayerObjectCommand(plr, false, "PushTo", obj);
return true;
}
}
// 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)
{
if (!this) return false;
var proc = GetProcedure();
// Some specific movement controls
if (!release)
{
// Jump control
if (ctrl == CON_Jump && 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);
}
// 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)
{
if (!this) return false;
// 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;
}
}
// ShiftCursor
// selects the next/previous crew member (that is not disabled)
global func ShiftCursor(int plr, bool back)
{
// get index of currently selected crew
var index = 0;
while (index < GetCrewCount(plr))
{
if (GetCursor(plr) == GetCrew(plr,index)) break;
index++;
}
// a short explanation here:
// when shifting the cursor to the next crew member, this crew member
// might be disabled via SetCrewEnabled. That is why we have to go to
// the crew after the next and perhaps even the crew after the next
// after the next. Also, we need to stop this skipping after all crew
// members have been checked in the special case that all crew members
// are disabled
var maxcycle = GetCrewCount(plr);
var cycle = 0;
do {
if (back)
{
--index;
if (index < 0) index = GetCrewCount(plr)-1;
}
else
{
++index;
if (index >= GetCrewCount(plr)) index = 0;
}
++cycle;
} while (!(GetCrew(plr,index)->GetCrewEnabled()) && cycle < maxcycle);
StopSelected();
UnselectCrew(plr);
return SetCursor(plr, GetCrew(plr,index));
}
// ShiftVehicle
// grabs the next/previous vehicle (if there is any)
global func ShiftVehicle(int plr, bool back)
{
if (!this) return false;
if (GetProcedure() != "PUSH") return false;
var lorry = GetActionTarget();
// get all grabbable objects
var objs = FindObjects(Find_OCF(OCF_Grab), Find_AtPoint(0,0), Find_Exclude(this));
// nothing to switch to (there is no other grabbable object)
if (GetLength(objs) <= 1) return false;
// find out at what index of the array objs the vehicle is located
var index = 0;
for(var obj in objs)
{
if (obj == lorry) break;
index++;
}
// get the next/previous vehicle
if (back)
{
--index;
if (index < 0) index = GetLength(objs)-1;
}
else
{
++index;
if (index >= GetLength(objs)) index = 0;
}
PlayerObjectCommand(plr, false, "Grab", objs[index]);
return true;
}
// GetEntranceObject
// Find an object with an entrance in front of this object whose entrance is at
// the right position
global func GetEntranceObject()
{
if (!this) return nil;
// object with an entrance on target position
var obj = FindObject(Find_OCF(OCF_Entrance), Find_AtPoint(0,0), Find_Exclude(this));
if (!obj) return nil;
var x = obj->GetDefCoreVal("Entrance","DefCore",0) + obj->GetX();
var y = obj->GetDefCoreVal("Entrance","DefCore",1) + obj->GetY();
var wdt = obj->GetDefCoreVal("Entrance","DefCore",2);
var hgt = obj->GetDefCoreVal("Entrance","DefCore",3);
// entrance is on the vehicle?
if (!Inside(GetX(), x, x+wdt)) return nil;
if (!Inside(GetY(), y, y+hgt)) return nil;
return obj;
}
// Temporarily used for Debugging!
// 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_Jump) return "Jump";
if (ctrl == CON_MenuLeft) return "MenuLeft";
if (ctrl == CON_MenuRight) return "MenuRight";
if (ctrl == CON_MenuOK) return "MenuOK";
if (ctrl == CON_MenuUp) return "MenuUp";
if (ctrl == CON_MenuDown) return "MenuDown";
if (ctrl == CON_NextItem) return "NextItem";
if (ctrl == CON_PreviousItem) return "PreviousItem";
if (ctrl == CON_Use) return "Use";
if (ctrl == CON_Drop) return "Drop";
if (ctrl == CON_NextCrew) return "NextCrew";
if (ctrl == CON_PreviousCrew) return "PreviousCrew";
if (ctrl == CON_MenuCancel) return "MenuCancel";
if (ctrl == CON_PlayerMenu) return "PlayerMenu";
if (ctrl == CON_GrabNext) return "GrabNext";
if (ctrl == CON_GrabPrevious) return "GrabPrevious";
if (ctrl == CON_Grab) return "Grab";
if (ctrl == CON_Ungrab) return "Ungrab";
if (ctrl == CON_AllCrew) return "AllCrew";
if (ctrl == CON_PushEnter) return "PushEnter";
if (ctrl == CON_Enter) return "Enter";
if (ctrl == CON_Exit) return "Exit";
if (ctrl == CON_Hotkey0) return "Hotkey0";
if (ctrl == CON_Hotkey1) return "Hotkey1";
if (ctrl == CON_Hotkey2) return "Hotkey2";
if (ctrl == CON_Hotkey3) return "Hotkey3";
if (ctrl == CON_Hotkey4) return "Hotkey4";
if (ctrl == CON_Hotkey5) return "Hotkey5";
if (ctrl == CON_Hotkey6) return "Hotkey6";
if (ctrl == CON_Hotkey7) return "Hotkey7";
if (ctrl == CON_Hotkey8) return "Hotkey8";
if (ctrl == CON_Hotkey9) return "Hotkey9";
if (ctrl == CON_PlayerHotkey0) return "PlayerHotkey0";
if (ctrl == CON_PlayerHotkey1) return "PlayerHotkey1";
if (ctrl == CON_PlayerHotkey2) return "PlayerHotkey2";
if (ctrl == CON_PlayerHotkey3) return "PlayerHotkey3";
if (ctrl == CON_PlayerHotkey4) return "PlayerHotkey4";
if (ctrl == CON_PlayerHotkey5) return "PlayerHotkey5";
if (ctrl == CON_PlayerHotkey6) return "PlayerHotkey6";
if (ctrl == CON_PlayerHotkey7) return "PlayerHotkey7";
if (ctrl == CON_PlayerHotkey8) return "PlayerHotkey8";
if (ctrl == CON_PlayerHotkey9) return "PlayerHotkey9";
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)
{
follow_clonk->SetCommand(command,target,tx,ty);
}
}
return true;
}
// ObjectComStop
// Stop action and ComDir
global func ObjectComStop()
{
SetComDir();
SetAction("Idle");
if (!SetAction("Walk")) return false;
SetXDir(); SetYDir();
return true;
}
// ObjectComLetGo
// Let go from scaling or hangling
global func ObjectComLetGo(int vx, int vy)
{
if (!SetAction("Jump")) return false;
SetXDir(vx); SetYDir(vy);
return true;
}

View File

@ -0,0 +1,538 @@
[ControlDefs]
[ControlDef]
Identifier=None
# Summary
# --------
# Left Right Up Down Jump
# Throw Drop Use
#
# NextItem PreviousItem
#
# Grab Ungrab GrabPrevious GrabNext PushEnter
#
# Enter Exit
#
# Hotkey1 Hotkey2 Hotkey3 Hotkey4 Hotkey5 Hotkey6 Hotkey7 Hotkey8 Hotkey9
# Hotkey0
#
# NextCrew PreviousCrew AllCrew
# PlayerHotkey1 PlayerHotkey2 PlayerHotkey3 PlayerHotkey4 PlayerHotkey5 PlayerHotkey6
# PlayerHotkey7 PlayerHotkey8 PlayerHotkey9 PlayerHotkey0
#
# MenuCancel MenuOK MenuDown MenuUp MenuLeft MenuRight
# PlayerMenu
# Movement
[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
Hold=1
[ControlDef]
Identifier=Down
GUIName=Down
GUIDesc=Move down
Hold=1
[ControlDef]
Identifier=Jump
GUIName=Jump
GUIDesc=Do the mario!
# Inventory
[ControlDef]
Identifier=Throw
GUIName=Throw
GUIDesc=Throw selected inventory item
Hold=1
RepeatDelay=5
InitialRepeatDelay=35
[ControlDef]
Identifier=Drop
GUIName=Drop
GUIDesc=Drop selected inventory item
Hold=1
RepeatDelay=5
InitialRepeatDelay=35
[ControlDef]
Identifier=NextItem
GUIName=Select next
GUIDesc=Select next inventory item
[ControlDef]
Identifier=PreviousItem
GUIName=Select previous
GUIDesc=Select previous inventory item
# Controlling (contents, vehicles, structures)
[ControlDef]
Identifier=Use
GUIName=Use
GUIDesc=Use selected or controlled item
Hold=1
RepeatDelay=1
[ControlDef]
Identifier=Grab
GUIName=Grab
GUIDesc=Grab vehicle
[ControlDef]
Identifier=Ungrab
GUIName=Let go
GUIDesc=Let go vehicle
[ControlDef]
Identifier=GrabPrevious
GUIName=Grab previous
GUIDesc=Grab the previous vehicle on the same spot
[ControlDef]
Identifier=GrabNext
GUIName=Grab next
GUIDesc=Grab the next vehicle on the same spot
[ControlDef]
Identifier=PushEnter
GUIName=Push into building
GUIDesc=Push the vehicle into the building
[ControlDef]
Identifier=Enter
GUIName=Enter
GUIDesc=Go into the building
[ControlDef]
Identifier=Exit
GUIName=Exit
GUIDesc=Exit the building
# Hotkeys (lower bar)
[ControlDef]
Identifier=Hotkey1
GUIName=Select first
GUIDesc=Select first item
[ControlDef]
Identifier=Hotkey2
GUIName=Select second
GUIDesc=Select second item
[ControlDef]
Identifier=Hotkey3
GUIName=Select third
GUIDesc=Select third item
[ControlDef]
Identifier=Hotkey4
GUIName=Select fourth
GUIDesc=Select fourth item
[ControlDef]
Identifier=Hotkey5
GUIName=Select fifth
GUIDesc=Select fifth item
[ControlDef]
Identifier=Hotkey6
GUIName=Select sixth
GUIDesc=Select sixth item
[ControlDef]
Identifier=Hotkey7
GUIName=Select seventh
GUIDesc=Select seventh item
[ControlDef]
Identifier=Hotkey8
GUIName=Select eighth
GUIDesc=Select eighth item
[ControlDef]
Identifier=Hotkey9
GUIName=Select ninth
GUIDesc=Select ninth item
[ControlDef]
Identifier=Hotkey0
GUIName=Select tenth
GUIDesc=Select tenth item
# the stuff for the second item comes later
# Crew
[ControlDef]
Identifier=NextCrew
GUIName=Next crew member
GUIDesc=Control next crew member
[ControlDef]
Identifier=PreviousCrew
GUIName=Previous crew member
GUIDesc=Control previous crew member
[ControlDef]
Identifier=AllCrew
GUIName=All the crew
GUIDesc=Control all crew member at the same time
[ControlDef]
Identifier=PlayerHotkey1
GUIName=Control first
GUIDesc=Control first crew member
[ControlDef]
Identifier=PlayerHotkey2
GUIName=Control second
GUIDesc=Control second crew member
[ControlDef]
Identifier=PlayerHotkey3
GUIName=Control third
GUIDesc=Control third crew member
[ControlDef]
Identifier=PlayerHotkey4
GUIName=Control fourth
GUIDesc=Control fourth crew member
[ControlDef]
Identifier=PlayerHotkey5
GUIName=Control fifth
GUIDesc=Control fifth crew member
[ControlDef]
Identifier=PlayerHotkey6
GUIName=Control sixth
GUIDesc=Control sixth crew member
[ControlDef]
Identifier=PlayerHotkey7
GUIName=Control seventh
GUIDesc=Control seventh crew member
[ControlDef]
Identifier=PlayerHotkey8
GUIName=Control eighth
GUIDesc=Control eighth crew member
[ControlDef]
Identifier=PlayerHotkey9
GUIName=Control ninth
GUIDesc=Control ninth crew member
[ControlDef]
Identifier=PlayerHotkey0
GUIName=Control tenth
GUIDesc=Control tenth crew member
# Menu control
[ControlDef]
Identifier=MenuOK
GUIName=OK
GUIDesc=Confirm in menus
Action=MenuOK
[ControlDef]
Identifier=MenuCancel
GUIName=Cancel
GUIDesc=Cancel or close the menu
Action=MenuCancel
[ControlDef]
Identifier=MenuLeft
GUIName=Left
GUIDesc=In the menu
Action=MenuLeft
[ControlDef]
Identifier=MenuRight
GUIName=Right
GUIDesc=In the menu
Action=MenuRight
[ControlDef]
Identifier=MenuUp
GUIName=Up
GUIDesc=In the menu
Action=MenuUp
[ControlDef]
Identifier=MenuDown
GUIName=Down
GUIDesc=In the menu
Action=MenuDown
# Extra buttons...
[ControlDef]
Identifier=PlayerMenu
GUIName=Player menu
GUIDesc=Open player menu
Action=Menu
[ControlSets]
# x = DONE
# Summary
# --------
# MenuLeft, Left x A
# MenuRight, Right x D
# MenuUp, Up, Jump x W
# MenuDown, Down x S
#
# NextItem x Scrollwheel-Up (now: Down)
# PreviousItem x Scrollwheel-Down (now: Up)
# Hotkey0-9 x (0-9)
#
# Use, Throw x Left mouse button (now: Left)
# Throw x Shift+Left mouse button (now: X+Left)
# Drop x S+Left mouse button (now: S+Left)
#
# PushEnter, GrabNext, Ungrab,
# Grab, Exit, Enter x Space
#
# PlayerHotkey0-9 x X+(0-9)
# NextCrew x E
# PreviousCrew x Q
#
# MenuOK x Return
# MenuCancel x Backspace
#
# PlayerMenu x End
#
# GrabPrevious, AllCrew x Unassigned
[ControlSet]
Name=Keyboard2Classic
[Assignment]
Key=A
Priority=50
Control=Left
[Assignment]
Key=D
Priority=50
Control=Right
[Assignment]
Key=S
Priority=50
Control=Down
[Assignment]
Key=W
Priority=50
Control=Up
[Assignment]
Key=W
Priority=10
Control=Jump
[Assignment]
Key=A
Priority=100
Control=MenuLeft
[Assignment]
Key=D
Priority=100
Control=MenuRight
[Assignment]
Key=S
Priority=100
Control=MenuDown
[Assignment]
Key=W
Priority=100
Control=MenuUp
[Assignment]
Key=Return
Control=MenuOK
[Assignment]
Key=Back
Control=MenuCancel
[Assignment]
Key=Space
Priority=45
Control=PushEnter
[Assignment]
Key=Space
Priority=40
Control=GrabNext
[Assignment]
Key=Space
Priority=35
Control=Ungrab
[Assignment]
Key=Space
Priority=30
Control=Grab
[Assignment]
Key=Space
Priority=25
Control=Exit
[Assignment]
Key=Space
Priority=20
Control=Enter
[Assignment]
Key=0
Control=Hotkey0
[Assignment]
Key=1
Control=Hotkey1
[Assignment]
Key=2
Control=Hotkey2
[Assignment]
Key=3
Control=Hotkey3
[Assignment]
Key=4
Control=Hotkey4
[Assignment]
Key=5
Control=Hotkey5
[Assignment]
Key=6
Control=Hotkey6
[Assignment]
Key=7
Control=Hotkey7
[Assignment]
Key=8
Control=Hotkey8
[Assignment]
Key=9
Control=Hotkey9
[Assignment]
Key=X,0
Control=Hotkey0
[Assignment]
Key=X,1
Control=PlayerHotkey1
[Assignment]
Key=X,2
Control=PlayerHotkey2
[Assignment]
Key=X,3
Control=PlayerHotkey3
[Assignment]
Key=X,4
Control=PlayerHotkey4
[Assignment]
Key=X,5
Control=PlayerHotkey5
[Assignment]
Key=X,6
Control=PlayerHotkey6
[Assignment]
Key=X,7
Control=PlayerHotkey7
[Assignment]
Key=X,8
Control=PlayerHotkey8
[Assignment]
Key=X,9
Control=PlayerHotkey9
[Assignment]
Key=Q
Control=PreviousCrew
[Assignment]
Key=E
Control=NextCrew
[Assignment]
Key=End
Control=PlayerMenu
[Assignment]
Key=Up
Control=PreviousItem
[Assignment]
Key=Down
Control=NextItem
[Assignment]
Key=Left
Priority=100
Control=Use
[Assignment]
Key=Left
Priority=50
Control=Throw
[Assignment]
Key=X,Left
Control=Throw
[Assignment]
Key=S,Left
Control=Drop

View File

@ -18,9 +18,9 @@ Rules=CNMT=1
[Player1]
Wealth=50,0,0,250
Crew=CLNK=1
Buildings=HUT2=1
Material=FLAG=1
Crew=CLNK=2
Buildings=TWKS=1
Material=ROCK=3;LORY=2
Knowledge=HUT2=0;HUT3=0;ELEV=0;SAWM=0;CST1=0
HomeBaseMaterial=CNKT=5;LOAM=5;WOOD=10;METL=5;FLNT=5;TFLN=5;LORY=1;CATA=1;BALN=1;CLNK=2
HomeBaseProduction=CNKT=5;LOAM=5;WOOD=10;METL=5;FLNT=5;TFLN=5;LORY=1;CATA=1

View File

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

View File

@ -57,6 +57,7 @@
#include <C4ObjectMenu.h>
#include <C4GameLobby.h>
#include <C4ChatDlg.h>
#include <C4PlayerControl.h>
#include <C4MouseControl.h>
#include <C4PXS.h>
#include <C4MessageInput.h>
@ -594,6 +595,8 @@ void C4Game::Clear()
KeyboardInput.Clear();
SetMusicLevel(100);
PlayList.Clear();
PlayerControlAssignmentSets.Clear();
PlayerControlDefs.Clear();
// global fullscreen class is not cleared, because it holds the carrier window
// but the menu must be cleared (maybe move Fullscreen.Menu somewhere else?)
@ -1836,7 +1839,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp)
{
if (!comp.fScenarioSection && comp.fExact)
{
pComp->Name("Game");
pComp->Name("Game");
pComp->Value(mkNamingAdapt(Time, "Time", 0));
pComp->Value(mkNamingAdapt(FrameCounter, "Frame", 0));
// pComp->Value(mkNamingAdapt(Control.ControlRate, "ControlRate", 0));
@ -1867,24 +1870,30 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp)
if (comp.fExact)
{
pComp->Value(mkNamingAdapt(Weather, "Weather"));
pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
pComp->Value(mkNamingAdapt(Landscape.Sky, "Sky"));
pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
pComp->Value(mkNamingAdapt(Landscape.Sky, "Sky"));
}
pComp->Value(mkNamingAdapt(mkNamingPtrAdapt(pGlobalEffects, "GlobalEffects"), "Effects"));
// scoreboard compiles into main level [Scoreboard]
if (!comp.fScenarioSection && comp.fExact)
pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard"));
if (!comp.fScenarioSection && comp.fExact)
{
// scoreboard compiles into main level [Scoreboard]
pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard"));
// Keyboard status of global keys synchronized for exact (runtime join) only; not for savegames,
// as keys might be released between a savegame save and its resume
//pComp->Value(GlobalPlayerControl);
}
if (comp.fPlayers)
{
assert(pComp->isDecompiler());
assert(pComp->isDecompiler());
// player parsing: Parse all players
// This doesn't create any players, but just parses existing by their ID
// Primary player ininitialization (also setting ID) is done by player info list
// Won't work this way for binary mode!
for (C4Player *pPlr=Players.First; pPlr; pPlr=pPlr->Next)
pComp->Value(mkNamingAdapt(*pPlr, FormatString("Player%d", pPlr->ID).getData()));
pComp->Value(mkNamingAdapt(mkParAdapt(*pPlr, comp.fExact), FormatString("Player%d", pPlr->ID).getData()));
}
}
@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
#include "C4PacketBase.h"
#include "C4PlayerInfo.h"
#include "C4Client.h"
#include "C4KeyboardInput.h"
#include "C4ObjectList.h"
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

View File

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

View File

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

View File

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

View File

@ -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;

View File

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

View File

@ -875,6 +875,17 @@ bool C4Object::ExecFire(int32_t iFireNumber, int32_t iCausedByPlr)
return true;
}
bool C4Object::BuyEnergy()
{
C4Player *pPlr = ::Players.Get(Base); if (!pPlr) return false;
if (!GetPhysical()->Energy) return false;
if (pPlr->Eliminated) return false;
if (pPlr->Wealth<Game.C4S.Game.Realism.BaseRegenerateEnergyPrice) return false;
pPlr->DoWealth(-Game.C4S.Game.Realism.BaseRegenerateEnergyPrice);
DoEnergy(+100,false,C4FxCall_EngBaseRefresh,Owner);
return true;
}
bool C4Object::ExecLife()
{
@ -1540,6 +1551,7 @@ void C4Object::Fling(FIXED txdir, FIXED tydir, bool fAddSpeed)
bool C4Object::ActivateEntrance(int32_t by_plr, C4Object *by_obj)
{
// Try entrance activation
if (OCF & OCF_Entrance)
if (!! Call(PSF_ActivateEntrance,&C4AulParSet(C4VObj(by_obj))))
@ -3058,421 +3070,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)
{

View File

@ -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(); }

View File

@ -618,10 +618,10 @@ bool ObjectComPut(C4Object *cObj, C4Object *pTarget, C4Object *pThing)
if (pTarget!=cObj->Contained)
if (!(pTarget->Def->GrabPutGet & C4D_Grab_Put))
{
// Was meant to be a drop anyway
if (ValidPlr(cObj->Owner))
if (::Players.Get(cObj->Owner)->LastComDownDouble)
return ObjectComDrop(cObj, pThing);
// Was meant to be a drop anyway - probably obsolete as controls are being revised
//if (ValidPlr(cObj->Owner))
// if (Game.Players.Get(cObj->Owner)->LastComDownDouble)
// return ObjectComDrop(cObj, pThing);
// No grab put: fail
return false;
}
@ -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

View File

@ -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()

View File

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

View File

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

View File

@ -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));
@ -1480,8 +1371,8 @@ void C4Player::CompileFunc(StdCompiler *pComp)
bool bForceFogOfWar = false;
pComp->Value(mkNamingAdapt(bForceFogOfWar, "ForceFogOfWar", false));
pComp->Value(mkNamingAdapt(ShowStartup, "ShowStartup", false));
pComp->Value(mkNamingAdapt(ShowControl, "ShowControl", false));
pComp->Value(mkNamingAdapt(ShowControlPos, "ShowControlPos", false));
pComp->Value(mkNamingAdapt(ShowControl, "ShowControl", 0));
pComp->Value(mkNamingAdapt(ShowControlPos, "ShowControlPos", 0));
pComp->Value(mkNamingAdapt(Wealth, "Wealth", 0));
pComp->Value(mkNamingAdapt(Points, "Points", 0));
pComp->Value(mkNamingAdapt(Value, "Value", 0));
@ -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;

View File

@ -26,6 +26,7 @@
#include <C4ObjectInfoList.h>
#include <C4InfoCore.h>
#include <C4ObjectList.h>
#include <C4PlayerControl.h>
const int32_t C4PVM_Cursor = 0,
C4PVM_Target = 1,
@ -72,7 +73,7 @@ class C4Player: public C4PlayerInfoCore
int32_t Team; // team ID - may be 0 for no teams
int32_t Color; // OldGfx color index
uint32_t ColorDw; // color as DWord for newgfx
int32_t Control;
int32_t ControlSet;
int32_t MouseControl;
int32_t Position;
int32_t PlrStartIndex;
@ -114,13 +115,10 @@ class C4Player: public C4PlayerInfoCore
C4IDList Knowledge;
C4IDList Magic;
// Control
C4PlayerControl Control;
C4Object *Cursor, *ViewCursor;
int32_t SelectCount;
int32_t SelectFlash,CursorFlash;
int32_t LastCom;
int32_t LastComDelay;
int32_t LastComDownDouble;
int32_t PressedComs;
int32_t CursorSelection,CursorToggled;
class C4GamePadOpener *pGamepad;
// Message
@ -171,9 +169,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);

View File

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

View File

@ -761,6 +761,8 @@ static bool FnSetActionData(C4AulObjectContext *cthr, long iData)
return true;
}
static C4Void FnSetComDir(C4AulObjectContext *cthr, long ncomdir)
static C4Void FnSetComDir(C4AulObjectContext *cthr, long ncomdir)
{
cthr->Obj->Action.ComDir=ncomdir;
@ -2427,24 +2429,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);
@ -2758,7 +2742,7 @@ static C4Object *FnGetCaptain(C4AulContext *cthr, long iPlr)
static bool FnSetCursor(C4AulContext *cthr, long iPlr, C4Object *pObj, bool fNoSelectMark, bool fNoSelectArrow, bool fNoSelectCrew)
{
C4Player *pPlr = ::Players.Get(iPlr);
if (!pPlr || (pObj && !pObj->Status)) return false;
if (!pPlr || (pObj && !pObj->Status) || (pObj && pObj->CrewDisabled)) return false;
pPlr->SetCursor(pObj, !fNoSelectMark, !fNoSelectArrow);
if (!fNoSelectCrew) pPlr->SelectCrew(pObj, true);
return true;
@ -3668,7 +3652,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 +5415,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 +5621,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 +5702,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 +5909,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "LocateFunc", FnLocateFunc);
AddFunc(pEngine, "PathFree", FnPathFree);
AddFunc(pEngine, "SetNextMission", FnSetNextMission);
AddFunc(pEngine, "GetPlayerControlState", FnGetPlayerControlState);
//FIXME new C4AulDefCastFunc(pEngine, "ScoreboardCol", C4V_C4ID, C4V_Int);
AddFunc(pEngine, "goto", Fn_goto);

View File

@ -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

View File

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

View File

@ -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) {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1444,10 +1444,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
@ -1498,24 +1498,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;
@ -1533,20 +1517,20 @@ void C4Viewport::DrawPlayerStartup(C4TargetFacet &cgo)
cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2+55,
cgo.Y+cgo.Hgt * 2/3 - 10 + DrawMessageOffset,
0,0);
if (Inside<int32_t>(pPlr->Control,C4P_Control_Keyboard1,C4P_Control_Keyboard4))
if (Inside<int32_t>(pPlr->ControlSet,C4P_Control_Keyboard1,C4P_Control_Keyboard4))
{
GfxR->fctKeyboard.Draw(cgo.Surface,
cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2,
cgo.Y+cgo.Hgt * 2/3 + DrawMessageOffset,
pPlr->Control-C4P_Control_Keyboard1,0);
pPlr->ControlSet-C4P_Control_Keyboard1,0);
iNameHgtOff=GfxR->fctKeyboard.Hgt;
}
else if (Inside<int32_t>(pPlr->Control,C4P_Control_GamePad1,C4P_Control_GamePad4))
else if (Inside<int32_t>(pPlr->ControlSet,C4P_Control_GamePad1,C4P_Control_GamePad4))
{
GfxR->fctGamepad.Draw(cgo.Surface,
cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2,
cgo.Y+cgo.Hgt * 2/3 + DrawMessageOffset,
pPlr->Control-C4P_Control_GamePad1,0);
pPlr->ControlSet-C4P_Control_GamePad1,0);
iNameHgtOff=GfxR->fctGamepad.Hgt;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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