diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..e7b97c5 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +.depends diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0a04c98 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +MAKEFILE_INCLUDED:=yes + +.PHONY: default all release + +default: all + +release: + @$(MAKE) RELEASE=yes + +include config/common.mk + diff --git a/README b/README new file mode 100644 index 0000000..417c5aa --- /dev/null +++ b/README @@ -0,0 +1,19 @@ + +Quadra Source Release +--------------------- +2000/07/25 + +This is a raw source release. Do not smoke near this package. + +Basic instructions: + + - go in the 'skelton' subdirectory (no, this is not a typo!) + - type 'make' + - go back in the root directory of the package + - type 'make' again + +There you go! There is a 'quadra' binary and a 'quadra.res' resource +file in the quadra/source directory. If not, then something went wrong. + +:-) + diff --git a/README.win32 b/README.win32 new file mode 100644 index 0000000..b0a519c --- /dev/null +++ b/README.win32 @@ -0,0 +1,39 @@ +Quadra version 1.1.4 for Windows +Copyright (c) 1998-2000 Ludus Design inc. All rights reserved. + +THANK YOU for downloading Quadra. This document contains installing information +and references to our support for Quadra. + +Quadra is an addictive puzzle and competitive, action-packed multiplayer game all-in-one! +Here are some key features: +- Incredibly smooth gameplay +- Recursive line clearing allowing blocks to combine in violent chain reactions that + will send your opponents crying back to their moms! +- 10 levels with colorful background images and matching sound effects +- Integrated CD music player +- Worldwide highscore table with recordings of your best performances +- Keep an eye on your opponents with the remote watch windows +- Chat window +- TCP/IP support for internet or LAN multiplayer games +- Cheat-free, lag-free and just plain free internet play +- Windows and Linux versions available + +PREVIOUS QUADRA VERSION + +If you had previously installed Quadra (such as version 1.1.1), the installer will +warn you that it has detected your previous installation. You MUST ensure that +Quadra and QSnoop are not currently running before proceding. You can look for +QSnoop in your Windows tray-bar. If it's there, close it by left clicking on the +small icon and select "Exit". + +You should choose to install in the same folder as your previous version. Your +recorded demo files and settings won't be overwritten. + +DIRECTX + +You'll need to have Microsoft DirectX version 5.0 (or more) for Quadra. If you have +Windows NT, you'll need to update to the latest service pack. Please visit our web +site for link and info about these requirements at: +http://www.ludusdesign.com/links.html +Quadra has been succesfully tested with DirectX 7.0. + diff --git a/config/.cvsignore b/config/.cvsignore new file mode 100644 index 0000000..c95dcac --- /dev/null +++ b/config/.cvsignore @@ -0,0 +1 @@ +local.mk diff --git a/config/common.mk b/config/common.mk new file mode 100644 index 0000000..8f76933 --- /dev/null +++ b/config/common.mk @@ -0,0 +1,33 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +.PHONY: clean niceclean deps + +TARGETS:= +CLEANS:= +DEPENDS:= +OBJECTS= + +-include config/local.mk + +include config/compiler.mk + +include $(wildcard */dir.mk) + +all: $(TARGETS) + +CLEANS+=$(shell find . -name '*.o' -print) + +clean: niceclean + rm -rf .depends $(CLEANS) + +niceclean: + rm -rf $(shell find . -name 'core' -print) $(shell find . -name '*~' -print) + +.depends: + @$(foreach DEP,$(DEPENDS),$(COMPILE.cc) -M $(DEP) | sed -e 's|^.*:|$(dir $(DEP))&|' >> $@ ;) + +ifneq ($(MAKECMDGOALS),clean) +include .depends +endif + diff --git a/config/compiler.mk b/config/compiler.mk new file mode 100644 index 0000000..4b54d3c --- /dev/null +++ b/config/compiler.mk @@ -0,0 +1,36 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +include config/target.mk + +SKELTON:=skelton + +CXXFLAGS+=-pipe -pedantic -Wall -Iinclude -I$(SKELTON)/include +LDFLAGS+=-L$(SKELTON)/lib + +OPTFLAGS+=-m486 -O6 +DEBUGFLAGS=-ggdb + +ifdef RELEASE +CXX=i386-glibc20-linux-g++ +CXXFLAGS+=-I/usr/i386-glibc20-linux/include/g++ $(OPTFLAGS) +LDFLAGS+=-L/usr/i386-glibc20-linux/lib -L/home/pp/lib/i386-glibc20-linux +else +CXXFLAGS+=$(DEBUGFLAGS) -D_DEBUG +endif + +ifdef SOCKS +CXXFLAGS+=-DSOCKS +endif + +ifeq "$(TARGET)" "linux" +CXXFLAGS+=-DUGS_LINUX -DUGS_LINUX_SVGA -DUGS_LINUX_X11 +#LDLIBS+=-L/usr/X11R6/lib -lX11 -lXext -lXpm -lvga -lvgagl -lz +#ifdef RELEASE +#LDLIBS+=-ldb +#else +#LDLIBS+=-ldb1 +#endif +else +# hmm, nothing else than linux is supported by this Makefile! +endif diff --git a/config/target.mk b/config/target.mk new file mode 100644 index 0000000..7af77f0 --- /dev/null +++ b/config/target.mk @@ -0,0 +1,4 @@ +# Makefile pour Quadra +# $Id$ + +TARGET:=linux diff --git a/demos/demo00.rec b/demos/demo00.rec new file mode 100644 index 0000000..45784ce Binary files /dev/null and b/demos/demo00.rec differ diff --git a/demos/demo01.rec b/demos/demo01.rec new file mode 100644 index 0000000..07d776f Binary files /dev/null and b/demos/demo01.rec differ diff --git a/demos/demo02.rec b/demos/demo02.rec new file mode 100644 index 0000000..ea696ff Binary files /dev/null and b/demos/demo02.rec differ diff --git a/demos/demo03.rec b/demos/demo03.rec new file mode 100644 index 0000000..40e4232 Binary files /dev/null and b/demos/demo03.rec differ diff --git a/fonts/courrier.fnt b/fonts/courrier.fnt new file mode 100644 index 0000000..e9d240e Binary files /dev/null and b/fonts/courrier.fnt differ diff --git a/fonts/font.fnt b/fonts/font.fnt new file mode 100644 index 0000000..3af2fc6 Binary files /dev/null and b/fonts/font.fnt differ diff --git a/help/config.jpg b/help/config.jpg new file mode 100644 index 0000000..04a6375 Binary files /dev/null and b/help/config.jpg differ diff --git a/help/connect.jpg b/help/connect.jpg new file mode 100644 index 0000000..8962caf Binary files /dev/null and b/help/connect.jpg differ diff --git a/help/creation.jpg b/help/creation.jpg new file mode 100644 index 0000000..4ee2e97 Binary files /dev/null and b/help/creation.jpg differ diff --git a/help/demo.jpg b/help/demo.jpg new file mode 100644 index 0000000..b4d2375 Binary files /dev/null and b/help/demo.jpg differ diff --git a/help/doze.gif b/help/doze.gif new file mode 100644 index 0000000..418f1f6 Binary files /dev/null and b/help/doze.gif differ diff --git a/help/doze_property.gif b/help/doze_property.gif new file mode 100644 index 0000000..a05986d Binary files /dev/null and b/help/doze_property.gif differ diff --git a/help/go.gif b/help/go.gif new file mode 100644 index 0000000..1b9b227 Binary files /dev/null and b/help/go.gif differ diff --git a/help/go_eng.gif b/help/go_eng.gif new file mode 100644 index 0000000..1171320 Binary files /dev/null and b/help/go_eng.gif differ diff --git a/help/help.html b/help/help.html new file mode 100644 index 0000000..a50058f --- /dev/null +++ b/help/help.html @@ -0,0 +1,939 @@ + + + + + + + Quadra aide - Ludus Design + + + + + + + +
+
+ +

+Bienvenue dans l'aide de Quadra +

+ +Le guide essentiel à la bonne compréhension du jeu, par Remz et Dada + +
+

+Ce guide est pour vous si vous vous posez une des questions suivantes:
+ +Comment on enlève pause?
+Pourquoi je m'appelle #1?
+Qu'est-ce qu'un frag?
+
+Si toutefois ces questions ne vous ont jamais troublé l'esprit (ou du moins, pas plus de 3 minutes), +vous avez tout intérêt à jeter un coup d'oeil aux guides de stratégie, car +comme le sage l'a dit: "Incomplète est votre formation.".
+Alors sans plus tarder, nous allons débuter les explications. Dans chacune des images présentées, +vous pourrez cliquer sur les divers éléments pour sauter directement à leurs descriptions. +

+ +

L'écran principal

+ +Voici à quoi ressemble l'écran principal de Quadra: +

+ +Partie solo +Partie multi-joueur +Centrale de démo +Pointages +Configurer joueur +Option +Enregistrer +Quitter +Site web Ludus Design +Information de version + + +
+
    +
  • Partie solo: Démarre une partie en mode solo, c'est-à-dire un +seul joueur qui devra tenter d'obtenir le pointage le plus élevé +possible. Un pointage assez élevé pourra être +inscrit dans la liste des meilleurs à travers le monde (Note: vous devrez avoir accès à l'Internet +pour obtenir la liste des pointages mondiaux). + +
  • Partie multi-joueur: Accède au menu des parties multi-joueurs. Vous +aurez alors la possibilité de faire une partie dite "locale" sur votre ordinateur, sur un +réseau local (LAN), ou sur Internet. + +
  • Centrale de démo: +La centrale de démo sert à écouter des parties enregistrées au préalable (appelées "démo"). +Vous pourrez également y faire jouer des parties téléchargées de l'Internet. + +
  • Pointages: Affiche la liste des meilleurs pointages mondiaux et locaux. + +
  • Configurer: Accède à l'écran de configuration des joueurs. + +
  • Option: Permet de modifier diverses options selon vos préférences. + +
  • Enregistrer: Si vous n'avez pas déjà enregistré votre +Quadra, c'est ici que ça ce passe. Si vous êtes enregistré, l'écran indique +quelques renseignements et liens vers le support technique. + +
  • Quitter: Quand il le faut absolument... + +
  • Information de version: Indique votre version actuelle de Quadra. + +
  • Site web Ludus Design: Version Windows seulement: Permet de démarrer +votre fureteur Internet vers la page principale du site de Ludus Design. +
+ +Pour vous déplacer parmi les options, utilisez la souris. Pour ceux qui préfèrent +le clavier, les touches de déplacement suivantes sont supportées:
+ TAB pour item suivant, +SHIFT + TAB pour item précédent, BARRE ou RETOUR pour effectuer une action. Vous +pouvez également utiliser les flèches de direction pour vous déplacer d'une zone à +l'autre. La touche ECHAP permet de reculer d'un menu, d'annuler ou de quitter.
+Utilisateurs Windows: vous pouvez en tout temps utiliser la combinaison + ALT + TAB ou CONTROL + ECHAP pour changer d'application pendant que vous êtes dans Quadra. Prenez garde si vous +êtes dans une partie multi-joueur sur Internet: si vous changer d'application, la partie ne sera +pas interrompue pour vous attendre :)
+ +Bon! Ne restez pas dans ce menu à ne rien faire! Vous devriez aller ajuster vos +préférences dans le menu Options. Vous pourrez choisir la vitesse de déplacement +de la souris, le mode de jeu de musique DC, etc. + +

L'écran des options

+

+ +Langue +Musique DC +Vitesse du curseur +Port TCP/IP +Adresse du serveur +Retour au menu + + +
+
    +
  • Langue: Vous avez le choix entre anglais et français. +
  • Musique DC: Ceci vous permet de décider le mode de fonctionnement +du lecteur de disque compact de Quadra. Les choix sont les suivants: +
      +
    • Pas de musique: Vous permet d'écouter le silence absolu. +
    • Auto-change de piste: À chaque changement de niveau pendant le jeu, le lecteur changera + de piste sur le DC. Ceci peut occasionner un léger délai entre les changements de niveau. + Lors d'une partie multi-joueur, la musique change si vous sélectionner un nouveau schème + visuel. +
    • Joue toutes les pistes: Le disque sera joué en entier à partir de la première + piste, ainsi de suite jusqu'à la fin. +
    + +
  • Vitesse du curseur: Vous pouvez indiquer un nombre entre +1 (très très lent) et 255 (rapide) pour le déplacement +du curseur de la souris. La valeur par défaut est 100, qui devrait ressembler +approximativement à une vitesse normale. +
  • Port TCP/IP: Utilisateurs avancés: +Cette option permet de déterminer le port TCP/IP utilisé par Quadra lors des parties multi-joueurs +LAN ou Internet. Si vous désirez changer ce numéro pour une raison ou une autre, sachez que +les autres ordinateurs devront utiliser exactement le même numéro de port si vous voulez +pouvoir les observer pendant une partie. Notez que vous pourriez quand même jouer avec eux, mais leurs +blocs vont simplement "apparaître" au bas du canevas au lieu de se déplacer en temps réel. +
    Dans la plupart des cas, vous devriez laisser la valeur par défaut qui est 3456. +
  • Adresse du serveur: Utilisateurs avancés: +Indique l'emplacement du serveur de partie sur Internet. Pour l'instant, il n'existe qu'un seul serveur +de partie publique, et il se trouve sur notre site, à l'adresse par défaut suivante: +http://ludusdesign.com/cgibin/qserv.pl. Notez qu'il ne s'agit pas d'un serveur Quadra, mais +bien simplement d'une liste des parties publiques actuellement en cours. Il n'est pas nécessaire +d'utiliser cette liste pour se connecter avec un autre ordinateur sur Internet. Vous pouvez laisser ce +champ vide, Quadra va utiliser la valeur par défaut lors de recherche sur Internet. +
  • Retour au menu: + Retourne au menu principal. Équivalent au clavier: ÉCHAP. +
+ +Vous voici donc dans le menu des options. La première chose à faire, c'est de choisir la +langue désirée. Le changement s'effectuera immédiatement après avoir cliqué +sur le bouton "Retour" ou "Back" dans le bas de l'écran. Ensuite, vous devriez +également ajuster à votre goût la vitesse de déplacement de la souris. Cliquez +sur la boîte d'entrée située à droite de "Vitesse du curseur de la souris (1-255)", +puis tapez-y le nombre voulu, suivi de RETOUR. Une fois cela terminé et que +la vitesse est acceptable, vous pouvez retourner au menu principal. Nous allons laisser les deux autres +options tranquilles pour l'instant.
+L'étape suivante est de configuré les informations du joueur. Allons rendez-vous dans ce +menu. + +

Configurer les joueurs

+

+ + Configurer joueur + Nom + Couleur + Ombre + Lisse + Vitesse clavier + Changes toutes + Change touche + Retour + + +
+
    +
  • Configurer joueur: Cliquez ici pour changer le numéro du +joueur à configurer. Il y a trois (3) joueurs par ordinateur. Si vous êtes plusieurs à +jouer à Quadra sur votre ordinateur, vous pourrez donc configurer jusqu'à concurrence de trois +joueurs avec des paramètres personnalisés. Si vous êtes seul, vous devriez normalement +configuré uniquement le joueur #1. + +
  • Nom : C'est ici que vous devrez entrer votre nom de joueur. Le nom +attribué par défaut est simplement #1 pour le joueur 1, et ainsi de suite. Si vous ne +changez pas votre nom, le jeu pourrait refuser de vous joindre à une partie car le nom "#1" +pourrait être déjà utilisé par quelqu'un d'autre qui a eu la même idée. +Vous pouvez entrer un nom ayant pour longueur maximale la largeur de la boîte d'entrée. + +
  • Couleur : Sélectionnez la couleur +de votre choix. Dans Quadra, la couleur désigne l'équipe. Autrement dit, +tous les joueurs ROUGE présents dans la partie seront dans la même équipe. En mode solo, +votre couleur n'a aucune importance. En mode multi-joueur, lorsque vous cliquerez sur le bouton +"Démarrer" pour commencer à jouer, vous aurez la possibilité de changer votre +couleur d'équipe en fonction des autres joueurs de cette partie. En d'autres termes, il est +préférable de sélectionner sa couleur lors de la partie, et non pas dans ce menu.
    + Note pour la souris: dans toutes les zones de sélection de Quadra, vous pouvez +appuyer sur le bouton de gauche de la souris pour avancer d'un choix, ou appuyer sur le bouton de droite +pour reculer d'un choix. + +
  • Ombre : Active ou désactive l'ombre +projetée par les blocs dans le bas du canevas de jeu. Cette option sert à aider à aligner +vos blocs lors de la partie. L'ombre représente un bloc de couleur foncée situé +exactement à l'endroit où votre bloc se dirige si vous le laissez tomber directement vers le +bas.
    + +
  • Lisse : Active ou désactive le +déplacement lisse des blocs. Lorsque vous déplacez le bloc, il "glisse" au lieu +d'apparaître immédiatement sur la case suivante. Cette option est purement esthétique +et n'affecte pas la vitesse ou la précision des déplacements. + +
  • Vitesse clavier : Spécifie +la vitesse de répétition des touches du clavier lors du jeu. Vous avez le choix entre +Lent, Normal, Rapide, ou Turbo. Si vous êtes débutant, les vitesses Lent ou Normal sont pour vous. +Si vous êtes surhumain, essayez Turbo. Dans tous les autres cas, la vitesse Rapide devrait être +juste à point pour vous. Prenez garde qu'un joueur qui joue à une vitesse haute peut déposer +beaucoup plus de blocs qu'un joueur qui joue lentement. Il est donc important de sélectionner la vitesse +la plus haute possible qui vous convienne. La vitesse de répétition affecte +à la fois les déplacements latéraux et la vitesse de dépôt des blocs. +La vitesse de rotation n'est pas altérée. + +
  • Changes toutes : Ce bouton +est un raccourci vers les 5 boutons de sélection de touche de jeu. Le jeu vous demandera successivement +les 5 touches. +
    Vous ne pouvez pas utiliser les touches suivantes: ÉCHAP, PAUSE, RETOUR. + +
  • Change touche : Sélectionnez +vos touches de contrôle pour le jeu. + +
  • Retour au menu: + Retourne au menu principal. Équivalent au clavier: ÉCHAP. +
+ +Une fois dans ce menu, vous devriez configurer au minimum le joueur #1. Ensuite, +vous êtes prêt à aller en jouer une! Mais avant d'affronter tous vos amis et adversaires, +il est conseillé d'aller essayer une partie en mode solo, histoire de vérifier si les touches +que vous avez sélectionnées sont efficaces, la vitesse de répétition +ainsi que les différentes options telle que l'ombre et le mouvement lisse. Donc, vous pouvez aller +jouer une partie solo, puis revenir dans cet écran pour ajuster vos paramètres, jusqu'à +ce que vous soyez fin prêt pour l'ultime combat. Allons débuter une partie solo. + +

La partie solo

+Pour débuter une partie solo, il faut choisir quelle configuration va être utilisée: +

+Démarrer quelqu'un +
+
    +
  • Démarrer quelqu'un: Cette liste de boutons +permet de choisir quel joueur va participer dans cette partie. Si vos 3 choix se nomment respectivement +#1, #2 et #3, vous pourriez revenir au menu de configuration des joueurs et inscrire votre nom :) Notez +qu'à tout moment pendant ou avant une partie, vous pouvez appuyer sur ÉCHAP +pour quitter et revenir au menu principal (ou au menu des pointages, si la partie était commencée) +
+ +

La partie solo (suite)

+Voici à quoi ressemble l'écran une fois la partie solo commencée: +

+ + Blocs suivants + Canevas de jeu + Totaux blocs + Totaux combos + Pointages + + +
+
    +
  • Blocs suivants: Dans le haut de +l'écran se situe les 3 blocs qui suivent celui que vous dirigez actuellement. Celui qui suit +immédiatement est le plus gros des 3 et se situe au centre du canevas. +
  • Canevas de jeu: Voici le fameux canevas de jeu. +C'est ici que vous dirigez et déposez vos blocs dans le but de faire des lignes horizontales. +
  • Totaux blocs: Dans le panneau de gauche, +vos avez le nombre de chaque type de bloc que vous avez déposé jusqu'à maintenant. +Il y a aussi le total des blocs d'indiqué au bas. + +
  • Totaux combos: Dans le panneau de droite +se trouve le nombre de combo de ligne que vous avez effectué au cours de cette partie. Chaque fois +que vous déposez un bloc qui complète une ligne horizontale, vous accumulez un "Simple". +Si vous faites deux lignes simultanément, vous accumulez un "Double", et ainsi de suite. + +
  • Pointages: En dessous du canevas, vos +avez vos informations actuelles: score, total de ligne, et niveau de difficulté. +
+ +Vous voici prêt à jouer! Déposez vos blocs et obtenez des points. C'est simple non? +Là où les choses se compliquent, c'est lorsque vous essayez de faire beaucoup de points. +Et c'est là qu'interviennent les guides de stratégie pour partie solo et multi-joueur. Dans +ces guides, vous trouverez toute l'information requise pour faire un pointage monstre! Dans tous les cas, +lorsque vous finirez par trépasser (dans le jeu j'entends), vous irez droit au menu des meilleurs +pointages. + +

Le menu des meilleurs pointages

+

+ + Pointages mondiaux + Valider + Pointages locaux + Dernière solo + Rejouer + Retour + + +
+
    +
  • Pointages mondiaux: +Liste des 5 meilleurs pointages mondiaux. Cette liste peut être vide si vous n'avez pas d'accès +à l'Internet. Un bouton "Visionner" à droite de chaque pointage permet de regarder la partie +se dérouler devant vous. Vous aurez la possibilité de la faire jouer au ralenti (pour observer +en détail une séquence intéressante) ou en accéléré. + +
  • Valider: Attention: Ce bouton nécessite +une connexion à l'Internet. Si tout fonctionne correctement, Quadra va transmettre votre meilleur +pointage au serveur de partie publique, et ensuite télécharger les 5 meilleurs parties. Vous +devrez patientez quelques secondes durant le transfert. Si votre partie était excellente, il se +peut que vous soyez dans la liste des meilleurs parties à travers le monde, et tous pourront +regarder votre exploit à leur guise. + +
  • Pointages locaux: +Cette liste illustre les 5 meilleures parties qui ont été joué sur cet ordinateur. +Cliquez sur "Visionner" pour regarder les performances. + +
  • Dernière solo: Ce bouton vous permet +de regarder votre toute dernière partie. +
    Note: ce bouton ne sera pas présent si votre partie est dans les 5 meilleures. + +
  • Rejouer: Appuyez ici pour recommencer une +nouvelle partie solo. + +
  • Retour: + Retourne au menu principal. Équivalent au clavier: ÉCHAP. +
+ +Maintenant que vous savez jouer à Quadra, et que vous êtes même en mesure de faire +des pointages époustouflants, vous avez sûrement envie d'aller affronter quelques autres +personnes! Dirigeons-nous vers le menu multi-joueur pour en apprendre son fonctionnement. + +

Organiser une partie multi-joueur

+Avant de commencer, il faut faire un choix: +

+ + Mode local (pas de réseau) + TCP/IP réseau LAN + TCP/IP réseau Internet + + +
+
    +
  • Mode local (pas de réseau): +Ce bouton sert à créer une partie multi-joueur sur votre ordinateur. Vous pouvez jouer jusqu'à +3 joueurs simultanément (en souhaitant que vous ayez tous des touches de clavier différentes). +Vous n'avez pas besoin d'être connecté en réseau pour utiliser ce mode de jeu.
    +Astuce: on peut aussi utiliser ce mode pour se pratiquer seul. Étant donné que la partie sera +en mode multi-joueur, vous pourrez recommencer aussi souvent que vous le voulez, ce qui est plus pratique +que le mode solo. Par contre, vous ne pourrez pas obtenir votre nom de la liste des meilleurs pointages. + +
  • TCP/IP réseau LAN: +Cliquez sur ce bouton-ci pour jouer une partie en réseau avec d'autres ordinateurs sur un réseau +local. Vous devez avoir le protocole TCP/IP installé dans votre système d'exploitation.
    +Utilisateurs Windows: pour savoir ce protocole est installé dans votre système, +ouvrez les propriétés du réseau du panneau de configuration. +Vous devriez obtenir une fenêtre qui ressemble à celle-ci:
    +

    +Vérifiez que vous avez bien un protocole TCP/IP dans la liste. Celui qui s'appelle "Carte d'accès distant" +sert à vous connecter à l'Internet. Si vous avez une carte réseau dans votre ordinateur, +il est possible de voir un deuxième TCP/IP (ou même davantage). Si vous avez du trouble à +faire fonctionner Quadra dans ce mode multi-joueur, envoyez un courriel à notre support technique: +support@ludusdesign.com + +
  • TCP/IP réseau Internet: +Semblable au mode TCP/IP LAN, ce mode de jeu vous permet de vous connecter avec d'autres ordinateurs en +utilisant le protocole TCP/IP. En plus, ce mode offre la possibilité de créer des parties +dites "publiques" qui pourront être vues à travers le monde entier. +
+ +

Créer une partie multi-joueur

+L'écran de création d'une partie multi-joueur local, LAN ou Internet: +

+ + Nom + Publique Oui/Non + Type de partie + Changement de niveau + Niveau de départ + Lignes pour attaquer + Termine quand + Enregistre la partie + Créer + Enregistrer + Retour + + +
+
    +
  • Nom: +Entrez le nom de la partie.
    +Note: en mode local, cet item n'est pas disponible. + +
  • Publique Oui/Non: +Permet ou non la visibilité de votre partie à travers l'Internet. Quadra "parlera" +avec le serveur de partie publique indiqué dans les options +pour signaler la création de votre partie. Ainsi, les autres personnes qui consulteront la +liste des parties disponibles verront la vôtre. Choisissez "Non" si vous ne voulez pas la rendre +publique. Notez que vos amis pourront tout de même se joindre à votre partie si vous leur +communiquer votre adresse IP. Cela vous permet de faire des parties privées.
    +Note: en mode local et en mode LAN, cet item n'est pas disponible. + +
  • Type de partie: +Il y a 2 types de parties: +
    • Partie libre: Obtenez des points (frags) pour chaque adversaire que vous tuez. Si vous mourez, +vous n'aurez alors qu'à appuyer sur une de vos touches pour recommencer de plus bel.
      + Utilisateurs avancés: pour comprendre le fonctionnement des frags, + cliquez ici. +
    • Dernier survivant: Similaire au mode Partie libre, excepté que vous ne pouvez pas +recommencer immédiatement après avoir été tué. Vous devrez attendre la fin +de la manche, qui se termine lorsqu'un joueur (ou son équipe) devient le dernier survivant. Ensuite, +tous les joueurs seront automatiquement redémarrés pour la manche suivante. +Pendant que vous patientez, profitez-en pour observer vos ennemis ou pour encourager vos coéquipiers qui +sont encore en vie :) +
    + +
  • Changement de niveau: +Détermine si le niveau de difficulté du jeu augmente à chaque fois que vous +avez obtenu 15 lignes. Cette option peut s'avérer utile si vous jouez contre quelqu'un de trop +fort et qui semble invincible (surtout en mode "Partie libre); ainsi, plus il joue longtemps, plus son +niveau de difficulté augmentera, et il finira bien par crever! + +
  • Niveau de départ: +Sélectionnez le niveau de difficulté de départ. Plus vous sélectionnez un +niveau élevé, plus la vitesse de descente des blocs sera rapide. +Conseil: vous devriez laisser le niveau à 1 si vous jouez avec des débutants. + +
  • Lignes pour attaquer: +Le nombre de ligne requis pour attaquer les adversaires. La valeur par défaut étant un +"double". Ce qui signifit que tout ce qui est moins qu'un double (exemple: un simple) n'attaquera +pas les adversaires. Plus vous sélectionnez un nombre élevé, plus il sera difficile +d'attaquer ses adversaires. +
    Conseil: vous devriez laisser le niveau à "double" si vous jouez avec des débutants. +
    Autre conseil: vous pourrez expérimenter un niveau plus élevé si vous voulez jouer +contre plusieurs adversaires de calibre expert. Par exemple, si vous choisissez "5-lignes" comme minimum, vous +vous trouverez alors à "calmer" beaucoup les petits échanges de lignes. + +
  • Termine quand: +Indique à quel moment la partie doit prendre fin. Vous avez le choix parmi: +
    • Jamais: le serveur (créateur) de la partie devra la terminer manuellement. +
    • Après "X" frags: lorsqu'une équipe aura atteint X frags. +
    • Après "X" minutes: un compte à rebours indiquera le temps restant. +
    + +
  • Enregistre la partie: +Si vous désirez enregistrer cette partie pour la conserver. Entrez un nom de fichier dans la +boîte de texte qui apparaîtra.
    +Note: Quadra ajoute automatiquement l'extension .REC aux fichiers de démo. + +
  • Créer: +Créer la partie et vous transporte dans l'écran de jeu multi-joueur. + +
  • Enregistrer: +Sert à conserver ces paramètres de création de partie par défaut. + +
  • Retour: +Retourne au menu précédent. +
+ +

Multi-joueur TCP/IP: Créer ou joindre

+L'écran de connexion pour partie multi-joueur LAN ou Internet: +

+ + Créer + Rafraîchir la liste + Informations d'une partie + Liste des parties + Liste des joueurs d'une partie + Joindre + Connecter à une adresse + Information IP + Retour + + +
+
    +
  • Créer: +Cliquez là-dessus pour créer une nouvelle partie dont vous serez le serveur et l'administrateur. + +
  • Rafraîchir la liste: +Sert à mettre à jour la liste des parties. + +
  • Informations d'une partie: +Donne les renseignements sur la partie sélectionnée dans la liste de gauche. + +
  • Liste des parties: +Ici sont listées les parties présentement disponibles. Cliquez sur le nom d'une partie +pour la sélectionner. + +
  • Liste des joueurs d'une partie: +Affiche la liste des joueurs actuellement présents dans la partie sélectionnée. + +
  • Joindre: +Cliquez ici pour vous joindre à une partie. + +
  • Connecter à une adresse: +Vous pouvez entrer directement une adresse IP ou une adresse URL à laquelle se trouve un serveur +de partie Quadra. +
    Note: en mode TCP/IP Internet, un bouton supplémentaire nommé "Carnet d'adresse" sera +disponible. Ce bouton vous permet d'entrer vos adresses IP les plus fréquemment utilisées. + +
  • Information IP: +Cliquez ici pour afficher votre adresse IP personnelle. Vous pouvez avoir plus d'une adresse selon votre +configuration réseau. + +
  • Retour: +Retourne au menu précédent. +
+ +Lorsque vous entrez dans cet écran, une recherche automatique des parties en cours s'effectue. Si +vous êtes sur le mode TCP/IP Internet, cette recherche à lieu en se connectant au serveur de +partie publique. Celui retourne comme résultat la liste des parties publiques existantes +actuellement. Si vous êtes en mode LAN, la recherche s'effectue à travers le réseau +local en utilisant un signal "broadcast". Il se peut qu'en fonction de votre configuration LAN, +le signal broadcast ne fonctionne pas. Il vous sera alors impossible de voir la liste des parties, mais vous +pourrez tout de même vous connecter à une partie. Il vous suffit d'entrer directement l'adresse +IP de l'ordinateur serveur dans la zone située dans le bas de l'écran. +
En principe, si la recherche a fonctionné et qu'il y a des parties disponibles, vous verrez alors +la liste apparaître. Il vous suffit de cliquer sur le nom d'une partie pour la sélectionner, puis +ensuite cliquez sur le bouton "Joindre la partie" pour vous y joindre. Note pour la souris: +vous pouvez faire un double-clique sur le nom de la partie. +
Note: si la partie que vous avez sélectionnée a été créée par +un serveur Quadra qui n'est pas exactement de la même version que le vôtre, vous ne pourrez pas joindre +celle-ci. Allez sur notre site web pour consulter les plus récentes mises à jour +de Quadra à l'adresse http://www.ludusdesign.com/quadra_fr.html. +
Dans tous les cas, quand vous aurez fini par joindre ou créer une partie, vous arriverez dans +l'écran du jeu multi-joueur, qui sera décrit ci-dessous. + +

L'écran de jeu multi-joueur

+Pour être flexible au maximum, l'écran de jeu multi-joueur est en fait composé de +3 panneaux indépendants. Chacun de ces panneaux peut être fermé à tout +moment en cliquant sur le bouton "Fermer" situé directement au-dessus. Vous pouvez ainsi configurer +l'écran comme bon vous semble, en ouvrant et fermant ces panneaux. Voici le panneau principal: +

+ + Démarrer un joueur + Affiche les joueurs + Information blocs + Information lignes + Choisir le thème + Fenêtre de chat + Fonctions du réseau + Quitte + + +
+
    +
  • Démarrer un joueur: +Cliquer sur un de ces boutons pour démarrer un joueur. +
    Note: si un joueur a déjà été démarré mais que le panneau avait +été fermé, le mot "Démarrer" sera remplacé par "Continuer" pour signaler +que ce joueur est en cours. + +
  • Affiche les joueurs: +Affiche le panneau résumé de la liste des joueurs. C'est aussi dans ce panneau que vous +pourrez observer vos adversaires. + +
  • Information blocs: +Affiche le panneau des statistiques de blocs qui ont été déposés. + +
  • Information lignes: +Similaire au panneau des blocs, mais avec les lignes accomplies par joueur. + +
  • Choisir le thème: +Permet de sélectionner l'image de fond et les effets sonores en fonction du niveau de 1 à 10. +
    Note: pour pouvoir choisir un thème, vous devez l'avoir atteint préalablement +en mode solo! Par exemple, pour pouvoir choisir le thème du niveau 8, vous devez avoir réussi +à vous rendre jusqu'au niveau 8 dans une partie solo. + +
  • Fenêtre de chat: +Ouvre le panneau de dialogue (chat). +
    Note: ce bouton n'est visible qu'en mode multi-joueur TCP/IP (LAN ou Internet) + +
  • Fonctions du réseau: +Affiche le panneau des fonctions relatives au réseau. +
    Note: ce bouton n'est visible qu'en mode multi-joueur TCP/IP (LAN ou Internet) + +
  • Quitte: +Ceci met fin à votre joueur (ou vos joueurs) et vous transporte dans l'écran des résultats. +Si la partie ne contenait aucun joueur, ce bouton vous ramène au menu précédent. Notez +que si vous êtes serveur, ceci ne termine pas la partie: les autres ordinateurs pourront +continuer de jouer, tant que vous ne signalerez pas la fin de partie (voir le menu des résultats). +
    Note: ce bouton n'est visible que lorsque le panneau de droite est fermé. +
+ +Dès que vous entrez dans l'écran de jeu multi-joueur, vous pouvez commencer à manipuler +les panneaux à votre guise. Si la partie vient tout juste d'être créer, vous apercevrez +au centre de l'écran le mot "Pause" ressemblant un peu à ceci: +
+ +
+
Ce dernier indique que la partie est temporairement suspendue, ou encore quelle n'est pas commencée. +Seul le serveur peut signaler le départ en appuyant sur la touche PAUSE. Un +décompte de 5 secondes s'en suivra, puis la partie débutera pour de bon. Si vous voulez +temporairement suspendre la partie, vous n'avez qu'à appuyer sur PAUSE. Par la suite, aucun autre joueur ne pourra +enlever la fonction pause à part le serveur et vous-même. Autrement dit, si le serveur décide +de mettre pause, personne d'autre ne peut l'enlever. +
Note: le jeu débute sur PAUSE en mode multi-joueur TCP/IP (LAN ou Internet) seulement. + +

Multi-joueur: démarrer un joueur

+

+ + Démarrer + Choisir l'équipe + Joueurs dans l'équipe + + +
+
    +
  • Démarrer: +Démarre ce joueur dans l'équipe indiquée par la couleur sélectionnée. +Ce bouton demande la permission au serveur pour démarrer un nouveau joueur. Il se peut que le +serveur rejette votre demande. Voici les raisons possibles: +
      +
    • Le serveur refuse les joueurs: Le créateur de partie refuse les nouveaux joueurs. Peut être +que vous devriez tenter de dialoguer avec lui dans la fenêtre de chat pour demander l'autorisation. +
    • Nom déjà utilisé: Un joueur utilisant exactement le même nom que vous existe +déjà dans la partie. Vous devez changer de nom pour pouvoir joindre cette partie, ou +demander à l'administrateur d'éliminer le joueur pour vous laisser la place. +
    • La partie est terminée: Si cette partie est déjà terminée, vous ne pouvez +plus joindre de joueur. +
    + +
  • Choisir l'équipe: +Cliquez ici pour changer d'équipe avant de vous joindre à la partie. Vous avez l'opportunité +de constater quels joueurs sont déjà dans la partie, ou encore quelle équipe semble le plus avoir +besoin de votre coup de main. + +
  • Joueurs dans l'équipe: +Cette liste illustre les joueurs déjà présents dans l'équipe +que vous avez présentement sélectionnée. +
+ +Particularité: si vous étiez déjà dans une partie et que, pour une raison ou pour +une autre, votre connexion a été interrompue, vous pouvez revenir dans la partie. Votre nom +devrait être déjà dans la liste des joueurs (à moins que le serveur n'aie décidé +de vous éliminer). Pour continuer en conservant vos statistiques, il s'agit de re-joindre la même +partie en utilisant la même couleur d'équipe que vous utilisiez déjà. Sinon, +Quadra considère que vous êtes un nouveau joueur, et va remettre toutes les statistiques à zéro. +
Toutefois, au moment où vous aviez sorti (volontairement ou involontairement), vous allez être +considéré comme quelqu'un de mort. En mode dernier survivant, cela pourrait avoir pour effet de +terminer une manche, considérant que vous étiez le seul adversaire encore vivant. + +

Multi-joueur: un joueur en cours de partie

+Ce panneau devrait vous sembler familier si vous avez lu la section sur le mode solo: +

+ + Blocs suivants + Ferme + Canevas de jeu + Lignes en attente + Vos informations + + +
+
    +
  • Blocs suivants: +Cette zone illustre les 3 blocs suivant celui que vous contrôler présentement. + +
  • Ferme: +Ce bouton ferme le panneau et retourne au panneau principal. + +
  • Canevas de jeu: +Cette dans cette région que vous déposez allègrement vos blocs. + +
  • Lignes en attente: +Cette zone indique combien de lignes vous allez recevoir de vos adversaires. Plus la colonne devient +haute, plus vous recevrez de lignes lorsque vous déposerez votre prochain bloc. Utilisez cette +information judicieusement, votre survie en dépendra! + +
  • Vos informations: +Dans le bas de l'écran, vous verrez vos informations courantes: +
      +
    • Score: Indique votre pointage actuel. +
    • Lignes: Un premier champ indique le nombre de ligne accomplie au cours de cette vie, tandis que l'autre +indique le nombre cumulatif de ligne accompli au cours de la partie. +
    • Frags: Indique le nombre d'adversaire que vous avez tué sans pitié. Pour +tuer quelqu'un, il faut lui envoyer des lignes et provoquer son décès. Pour savoir exactement +comment fonctionne les frags, consultez le guide de stratégie multi-joueur. +
    • Morts: Indique le nombre de fois que vous êtes mort. +
    • Niveau: Votre niveau de difficulté. +
    +
+ +

Multi-joueur: afficher les joueurs

+

+ + Liste des joueurs + Affiche quoi + + +
+
    +
  • Liste des joueurs: +Liste des joueurs actifs dans la partie présentement. Les joueurs provenant des autres ordinateurs +connectés à cette partie seront inscrits dans un bouton. Vous pouvez cliquer sur le nom d'un +de ces joueurs, puis ensuite cliquer sur le bouton "Observe sélectionnés" pour les voir en pleine action. Si vous +sélectionnez seulement 1 joueur, il sera affiché en pleine grandeur dans le panneau. Si vous +en sélectionnez 2,3 ou 4, ils seront affichés en format réduit. +
    Note: vous ne pouvez pas observer un joueur qui a été déconnecté ou qui +a quitté la partie. + +
  • Affiche quoi: +Cliquez ici pour changer quel renseignement sera affiché à droite de chaque nom de joueur. +Vous pouvez choisir parmi: Frags & Morts, Score, Blocs, et Lignes. +
+ +

Multi-joueur: affiche les statistiques de blocs

+

+ + Totaux par bloc + Joueur vs joueur + + +
+
    +
  • Totaux par bloc: +Indique le nombre de bloc de chaque type qui ont été déposé au cours de la +partie, ainsi que le total de tous les blocs. Vous pouvez comparer la vitesse d'un joueur par rapport à +un autre en regardant lequel a déposé le plus de bloc. Toutefois, il est possible que les deux +joueurs n'aient pas commencé à jouer au même moment. + +
  • Joueur vs joueur: +Cliquez sur le nom d'un joueur pour passer au joueur suivant. +
+ +

Multi-joueur: affiche les statistiques de lignes

+

+ + Totaux des lignes + Joueur vs joueur + + +
+
    +
  • Totaux des lignes: +Indique le nombre de combo de ligne ont été exécuté par chaque joueur. +Le total des lignes est aussi indiqué dans le bas. + +
  • Joueur vs joueur: +Cliquez sur le nom d'un joueur pour passer au joueur suivant. +
+ +

Multi-joueur: fenêtre de dialogue (chat)

+

+ + Zone de dialogue + Zone d'entrée + De qui + A qui + + +
+
    +
  • Zone de dialogue: +Affiche le dialogue qui a eu lieu. Les lignes de textes provenant des autres joueurs seront en +fonction de la couleur de leurs équipes. + +
  • Zone d'entrée: +Cliquez dans cette zone pour entrer une ligne de texte. Si vous êtes en pleine partie, tâchez +de taper rapidement car vous ne pourrez plus contrôler votre joueur pendant que vous entrez votre message! +
    Raccourci au clavier: Appuyez sur RETOUR pour commencer à taper, puis RETOUR +une deuxième fois pour l'émettre. Vous pouvez faire ECHAP pour annuler la ligne de texte en cours. + +
  • De qui: +Cette zone vous permet de modifier la provenance de vos messages. Étant donné que vous +pouvez configurer jusqu'à 3 joueurs par ordinateur, vous pouvez décider de "qui" proviennent +les messages que les autres ordinateurs recevront de vous. + +
  • A qui: +Détermine à qui vous désirez émettre vos messages. Vous pouvez choisir TOUS +pour l'envoyer à tous les autres joueurs, ou choisir une couleur d'équipe pour l'envoyer +uniquement à cette équipe. +
+ +

Multi-joueur: les fonctions réseau

+

+ + Ferme joueur + Ferme connexion + Accepte joueur + Accepte connexion + Tester Ping + M.A.J. des joueurs observés + Information IP + + +
+
    +
  • Ferme joueur: +Affiche la liste des joueurs présents dans la partie, et permet de les éliminer. +
    Note: ce bouton n'est visible que sur l'ordinateur serveur. + +
  • Ferme connexion: +Affiche la liste des connexions réseaux en cours, et vous permet de les éliminer. +Si vous fermez une connexion, vous vous trouverez en même temps à éliminer les joueurs +qui étaient sur cet ordinateur. +
    Note: ce bouton n'est visible que sur l'ordinateur serveur. + +
  • Accepte joueur: +Permet d'empêcher les nouveaux joueurs de s'ajouter à votre partie. Tant que cette option sera +à "Non", les joueurs qui tenteront de se joindre recevront un message disant "Le serveur a refusé +votre demande". Néanmoins, ils pourront dialoguer avec vous grâce à la fenêtre de chat. +
    Note: cette option n'est visible que sur l'ordinateur serveur. + +
  • Accepte connexion: +Permet d'empêcher toute tentative de connexion à votre partie. Utilisez cette fonction pour +assurer votre sécurité au cas où des personnes malfaisantes tenteraient de vous +nuire. Prenez garde toutefois que personne ne pourra se connecter pendant que cette option est +à "Non". +
    Note: cette option n'est visible que sur l'ordinateur serveur. + +
  • Tester Ping: +Cliquez sur ce bouton pour connaître le temps réponse entre votre ordinateur et le serveur. +Si vous avez un temps dépassant 2000 millisecondes, votre connexion est lente, et tous les +événements du jeu risquent d'arriver en retard. +Si vous êtes vous-même le serveur, ce temps devrait être 10 millisecondes environ. + +
  • M.A.J. des joueurs observés: +Permet de décider la fréquence des mises à jour visuelles des joueurs observés. +Par exemple, si vous indiquez 10 et que vous observez votre adversaire dans un autre panneau, Quadra +transmettra 10 mises à jour par seconde afin que vous puissiez suivre ses déplacements. +Si votre connexion est lente, vous pouvez mettre 0 pour complètement désactiver les mises à +jour. Vous pourrez tout de même observer les autres joueurs, cependant, leurs blocs vont sembler +"apparaître" directement au lieu de se déplacer. Si vous êtes sur un réseau local +à grande vitesse, vous pouvez tenter de mettre une valeur plus élevée pour obtenir +des déplacements beaucoup plus fluides. Prenez notes des points suivants: +
      +
    • Le changement de vitesse de mise à jour s'effectue en temps réel. Cependant, si vous +voulez constater les changements de vitesses demandés par les autres joueurs, +vous devrez fermer puis rouvrir ces joueurs si vous les observiez déjà. +
    • A titre d'indication, sur Internet avec un modem 56.6Kps, vous devriez tenir ce nombre autour de 10 ou +moins. Sur un LAN, autour de 30 devrait être acceptable et assez fluide. Faites vos expérimentations! +
    • Il se peut que malgré que vous ayez demandé 50 mises à jour, un joueur en +particulier semble rester saccadé ou immobile. La raison est que cet ordinateur a peut-être +spécifié une vitesse de rafraîchissement d'environ 0, et que Quadra va s'ajuster selon +la vitesse la plus basse des 2 ordinateurs. +
    • Si vous observez des joueurs en format réduit (c'est à dire plusieurs joueurs dans le même +panneau), Quadra va automatiquement diminuer de moitié la vitesse de rafraîchissement, étant +donné la petite dimension des ces canevas. +
    + +
  • Information IP: +Indique votre adresse IP actuelle. + +

    Multi-joueur: l'écran des résultats

    +

    + + Titre des colonnes + Totaux par joueur + Signaler fin de partie + Changer de page + Retour + Zone de dialogue (chat) + + +
    +
      +
    • Titre des colonnes: +Cliquez sur le titre d'une colonne pour sélectionner l'ordre de tri des joueurs. + +
    • Totaux par joueur: +Cette région indique les divers totaux par joueurs avec des totaux cumulatifs par équipe. + +
    • Signaler fin de partie: +Ce bouton sert à signaler la fin de la partie à tous les joueurs actifs présentement. +Une fois la fin signalée, personne ne pourra se joindre à cette partie, car elle sera considérée +comme terminée. Si, lors de la création de cette partie, vous aviez choisi qu'elle se termine +au bout de X minutes ou lorsqu'une équipe atteint X frags, vous pouvez décider +de mettre fin avant l'échéance. Par ailleurs, ce bouton disparaîtra si la partie est +déjà terminée. +
      Note: ce bouton n'est visible que sur l'ordinateur serveur ET en mode multi-joueur TCP/IP + +
    • Changer de page: +Cliquez ici pour changer de page de statistiques. + +
    • Retour: +Ce bouton vous retourne au menu précédent et vous déconnecte de la partie. Si vous +étiez le serveur, ceci à pour effet d'émettre un message "Le serveur a été +déconnecté" aux autres ordinateurs, et cela interrompt leurs parties s'ils étaient +encore actifs. +
      Note: Les autres joueurs ne pourront plus dialoguer entre eux si vous sortez de l'écran des +résultats, car tous les messages doivent absolument passer par le serveur. + +
    • Zone de dialogue (chat): +Cette zone est exactement identique au panneau "fenêtre de chat" de l'écran de jeu. Vous +pouvez ainsi continuer à dialoguer avec les autres joueurs malgré que la partie soit terminée. +
      Note: ce panneau n'est visible qu'en mode multi-joueur TCP/IP (LAN ou Internet) +
    + +

    La centrale de démo

    +

    + +Répertoire en cours +Information +Liste des fichiers +Visionner démo +Retour + + +
    +
      +
    • Répertoire en cours: +Vous pouvez taper un répertoire différent de celui proposé par défaut (qui se +trouve à être le répertoire où est installé Quadra). + +
    • Information: Cette zone indique les renseignements +sur la partie sélectionnée dans la liste de gauche, ainsi que la liste des joueurs et leurs +statistiques lors de la fin de partie. + +
    • Liste des fichiers: Cette liste affiche tous les +fichiers *.REC trouvés dans le répertoire en cours. Cliquez sur un fichier pour le +sélectionner, ou double-cliquez pour passez directement au visionnement.
      +Note: vous remarquerez que les fichiers de démo appelés GLOBALx.REC et LOCALx.REC +correspondent aux démos des meilleurs pointages mondiaux et locaux. + +
    • Visionner démo: Ce bouton vous +transporte dans l'écran de jeu afin de regarder se dérouler la partie que vous avez +choisie. Des boutons de contrôle Vitesse lente et Vitesse rapide seront disponible +lors du déroulement de la partie. + +
    • Retour: +Retourne au menu principal. +
    + +
    +support@ludusdesign.com
    +
    ©1999 Ludus Design. Tous droits réservés.
    +
    Dernière mise à jour: 1999-08-23
    +
+ + diff --git a/help/help_eng.html b/help/help_eng.html new file mode 100644 index 0000000..8032cfc --- /dev/null +++ b/help/help_eng.html @@ -0,0 +1,976 @@ + + + + + + + Quadra help - Ludus Design + + + + + + + +
+
+ +

+Welcome to the Quadra help file +

+ +The essential guide to a good understanding of the game, by Remz and Dada + +
+

+This guide is for you if you ask yourself the following questions:
+ +How do I unpause the game?
+Why is my name "#1"?
+What is a frag?
+
+On the other hand, if those questions never troubled you (or at least +no more than 3 minutes) you should instead take a look at the +Strategy guides because, as the wise once said: +"Incomplete is your training.".
+So without further ado, we will begin explaining. +In any pictures below, you can click on the various options to jump +straight to the relevent description. +

+ +

The main menu

+ +Here's a screenshot of Quadra's main menu: +

+ +Single-player game +Multi-player game +Demo central +Highscores +Player setup +Options +Register +Quit +Ludus Design web site +Version information + + +
+
    +
  • Single-player game: +Start a single-player game where the object is to score as much points as possible. +A player scoring high enough can then have his performance recorded +in the Worldwide Highscores list (Note: you need Internet access +to enable this feature) + +
  • + Multi-player game: Access the multi-player games menu. +You will then be able to play a game either on a single computer, +a Local Area Network (LAN) or on the Internet. + +
  • + Demo central: +The demo central is used to play back games that were previously +recorded (called "demos"). You will also be able to play back games +downloaded from the Internet. + +
  • + Highscores: Display the Worldwide and local highscores list. + +
  • + Player setup: Access the players' configuration screen. + +
  • + Options: Modify various options according to your +preferences. + +
  • + Register: Allows you to register your copy of Quadra. +If you already have done so, choosing this menu option will give you +some informations about Ludus Design and links to our web site +and technical support. + +
  • + Quit: When you absolutely have to... + +
  • + Version information: Informs you of the version of Quadra +currently running. + +
  • + Ludus Design web site: +Windows version only: +Clicking there will start your default browser and go to Ludus Design's +internet site. +
+ +You can select menu options by clicking with the mouse. +The following keyboard keys are also supported:
+ TAB for next item, SHIFT + TAB for previous +item, Space or Enter to select an option. +You can also use the arrow keys to move from option to option. +The Escape key is used to go back to the previous menu, cancel a +selection or quit. +
+Windows users: +You can use the keyboard combo ALT + TAB or CONTROL + ESC to +switch to another windows application while Quadra is running. +Beware, though, that a multi-player game will not be paused while +you're fooling around with other applications :). +
+ +Ok, now stop gawking at this menu doing nothing! You should go +setup your preferences in the Options menu. +There, you'll be able to select mouse speed, CD music playing mode +and some other stuff. + +

The options menu

+

+ +Language +CD music +Mouse speed +TCP/IP port +Master server address +Back to main menu + + +
+
    +
  • Language: You can choose between English and French. +
  • CD Music: This allows you to select Quadra's cd player mode. +These are the possible choices: +
      +
    • No music: Listen to absolute silence. +
    • Auto-change track: Change CD track for each play level. + This can slightly slow down level changes. In multi-player games, + the track will change when you select a different graphics scheme. +
    • Loop all tracks: The CD will be played entirely, from the + first track to the last. +
    + +
  • Mouse speed: +Specify a number between 1 (utterly slow) and 255 (fast) for the +mouse's movement speed. The default setting of 100 is a typical speed. +
  • TCP/IP port: Advanced users: +This option changes the default TCP port for +multi-player LAN or internet games. Note that if you +change this number, it will probably be impossible +to watch other players (or have them watch you) place +blocks in real-time: the blocks will instead appear +instantly at the bottom of the screen. In most cases, +the default value of 3456 is perfectly ok. +
  • Master server address: + Advanced users: +Specify the master game server URL on the internet. +As of this writing, there is only one public master +server. It's situated on Ludus Design's website at +http://ludusdesign.com/cgibin/qserv.pl (this +is also the default value for this option). Note that +this is not a Quadra server where you would connect +and play the game: rather it's simply a listing of +public multi-player games actually going on. It is not +required to use this list to play multi-player games, +it's only a tool to help players that don't know each +other's IP address get together. +
  • + Back to main menu: Go back to the main +menu. Keyboard equivalent: ESC. +
+ +Vous voici donc dans le menu des options. La +première chose à faire, c'est de choisir +la langue désirée. Le changement +s'effectuera immédiatement après avoir +cliqué sur le bouton "Retour" ou +"Back" dans le bas de l'écran. Ensuite, +vous devriez également ajuster à votre +goût la vitesse de déplacement de la +souris. Cliquez sur la boîte d'entrée +située à droite de "Vitesse du +curseur de la souris (1-255)", puis tapez-y le +nombre voulu, suivi de RETOUR. +Une fois cela terminé et que la vitesse est +acceptable, vous pouvez retourner au menu principal. +Nous allons laisser les deux autres options +tranquilles pour l'instant.
L'étape +suivante est de configuré les informations du +joueur. Allons rendez-vous dans ce menu. + +

Configurer les joueurs

+

+ + Configurer joueur + Nom + Couleur + Ombre + Lisse + Vitesse clavier + Changes toutes + Change touche + Retour + + +
+
    +
  • Configurer joueur: Cliquez ici pour changer le numéro du +joueur à configurer. Il y a trois (3) joueurs par ordinateur. Si vous êtes plusieurs à +jouer à Quadra sur votre ordinateur, vous pourrez donc configurer jusqu'à concurrence de trois +joueurs avec des paramètres personnalisés. Si vous êtes seul, vous devriez normalement +configuré uniquement le joueur #1. + +
  • Nom : C'est ici que vous devrez entrer votre nom de joueur. Le nom +attribué par défaut est simplement #1 pour le joueur 1, et ainsi de suite. Si vous ne +changez pas votre nom, le jeu pourrait refuser de vous joindre à une partie car le nom "#1" +pourrait être déjà utilisé par quelqu'un d'autre qui a eu la même idée. +Vous pouvez entrer un nom ayant pour longueur maximale la largeur de la boîte d'entrée. + +
  • Couleur : Sélectionnez la couleur +de votre choix. Dans Quadra, la couleur désigne l'équipe. Autrement dit, +tous les joueurs ROUGE présents dans la partie seront dans la même équipe. En mode solo, +votre couleur n'a aucune importance. En mode multi-joueur, lorsque vous cliquerez sur le bouton +"Démarrer" pour commencer à jouer, vous aurez la possibilité de changer votre +couleur d'équipe en fonction des autres joueurs de cette partie. En d'autres termes, il est +préférable de sélectionner sa couleur lors de la partie, et non pas dans ce menu.
    + Note pour la souris: dans toutes les zones de sélection de Quadra, vous pouvez +appuyer sur le bouton de gauche de la souris pour avancer d'un choix, ou appuyer sur le bouton de droite +pour reculer d'un choix. + +
  • Ombre : Active ou désactive l'ombre +projetée par les blocs dans le bas du canevas de jeu. Cette option sert à aider à aligner +vos blocs lors de la partie. L'ombre représente un bloc de couleur foncée situé +exactement à l'endroit où votre bloc se dirige si vous le laissez tomber directement vers le +bas.
    + +
  • Lisse : Active ou désactive le +déplacement lisse des blocs. Lorsque vous déplacez le bloc, il "glisse" au lieu +d'apparaître immédiatement sur la case suivante. Cette option est purement esthétique +et n'affecte pas la vitesse ou la précision des déplacements. + +
  • Vitesse clavier : Spécifie +la vitesse de répétition des touches du clavier lors du jeu. Vous avez le choix entre +Lent, Normal, Rapide, ou Turbo. Si vous êtes débutant, les vitesses Lent ou Normal sont pour vous. +Si vous êtes surhumain, essayez Turbo. Dans tous les autres cas, la vitesse Rapide devrait être +juste à point pour vous. Prenez garde qu'un joueur qui joue à une vitesse haute peut déposer +beaucoup plus de blocs qu'un joueur qui joue lentement. Il est donc important de sélectionner la vitesse +la plus haute possible qui vous convienne. La vitesse de répétition affecte +à la fois les déplacements latéraux et la vitesse de dépôt des blocs. +La vitesse de rotation n'est pas altérée. + +
  • Changes toutes : Ce bouton +est un raccourci vers les 5 boutons de sélection de touche de jeu. Le jeu vous demandera successivement +les 5 touches. +
    Vous ne pouvez pas utiliser les touches suivantes: ÉCHAP, PAUSE, RETOUR. + +
  • Change touche : Sélectionnez +vos touches de contrôle pour le jeu. + +
  • Retour au menu: + Retourne au menu principal. Équivalent au clavier: ÉCHAP. +
+ +Une fois dans ce menu, vous devriez configurer au minimum le joueur #1. Ensuite, +vous êtes prêt à aller en jouer une! Mais avant d'affronter tous vos amis et adversaires, +il est conseillé d'aller essayer une partie en mode solo, histoire de vérifier si les touches +que vous avez sélectionnées sont efficaces, la vitesse de répétition +ainsi que les différentes options telle que l'ombre et le mouvement lisse. Donc, vous pouvez aller +jouer une partie solo, puis revenir dans cet écran pour ajuster vos paramètres, jusqu'à +ce que vous soyez fin prêt pour l'ultime combat. Allons débuter une partie solo. + +

La partie solo

+Pour débuter une partie solo, il faut choisir quelle configuration va être utilisée: +

+Démarrer quelqu'un +
+
    +
  • Démarrer quelqu'un: Cette liste de boutons +permet de choisir quel joueur va participer dans cette partie. Si vos 3 choix se nomment respectivement +#1, #2 et #3, vous pourriez revenir au menu de configuration des joueurs et inscrire votre nom :) Notez +qu'à tout moment pendant ou avant une partie, vous pouvez appuyer sur ÉCHAP +pour quitter et revenir au menu principal (ou au menu des pointages, si la partie était commencée) +
+ +

La partie solo (suite)

+Voici à quoi ressemble l'écran une fois la partie solo commencée: +

+ + Blocs suivants + Canevas de jeu + Totaux blocs + Totaux combos + Pointages + + +
+
    +
  • Blocs suivants: Dans le haut de +l'écran se situe les 3 blocs qui suivent celui que vous dirigez actuellement. Celui qui suit +immédiatement est le plus gros des 3 et se situe au centre du canevas. +
  • Canevas de jeu: Voici le fameux canevas de jeu. +C'est ici que vous dirigez et déposez vos blocs dans le but de faire des lignes horizontales. +
  • Totaux blocs: Dans le panneau de gauche, +vos avez le nombre de chaque type de bloc que vous avez déposé jusqu'à maintenant. +Il y a aussi le total des blocs d'indiqué au bas. + +
  • Totaux combos: Dans le panneau de droite +se trouve le nombre de combo de ligne que vous avez effectué au cours de cette partie. Chaque fois +que vous déposez un bloc qui complète une ligne horizontale, vous accumulez un "Simple". +Si vous faites deux lignes simultanément, vous accumulez un "Double", et ainsi de suite. + +
  • Pointages: En dessous du canevas, vos +avez vos informations actuelles: score, total de ligne, et niveau de difficulté. +
+ +Vous voici prêt à jouer! Déposez vos blocs et obtenez des points. C'est simple non? +Là où les choses se compliquent, c'est lorsque vous essayez de faire beaucoup de points. +Et c'est là qu'interviennent les guides de stratégie pour partie solo et multi-joueur. Dans +ces guides, vous trouverez toute l'information requise pour faire un pointage monstre! Dans tous les cas, +lorsque vous finirez par trépasser (dans le jeu j'entends), vous irez droit au menu des meilleurs +pointages. + +

Le menu des meilleurs pointages

+

+ + Pointages mondiaux + Valider + Pointages locaux + Dernière solo + Rejouer + Retour + + +
+
    +
  • Pointages mondiaux: +Liste des 5 meilleurs pointages mondiaux. Cette liste peut être vide si vous n'avez pas d'accès +à l'Internet. Un bouton "Visionner" à droite de chaque pointage permet de regarder la partie +se dérouler devant vous. Vous aurez la possibilité de la faire jouer au ralenti (pour observer +en détail une séquence intéressante) ou en accéléré. + +
  • Valider: Attention: Ce bouton nécessite +une connexion à l'Internet. Si tout fonctionne correctement, Quadra va transmettre votre meilleur +pointage au serveur de partie publique, et ensuite télécharger les 5 meilleurs parties. Vous +devrez patientez quelques secondes durant le transfert. Si votre partie était excellente, il se +peut que vous soyez dans la liste des meilleurs parties à travers le monde, et tous pourront +regarder votre exploit à leur guise. + +
  • Pointages locaux: +Cette liste illustre les 5 meilleures parties qui ont été joué sur cet ordinateur. +Cliquez sur "Visionner" pour regarder les performances. + +
  • Dernière solo: Ce bouton vous permet +de regarder votre toute dernière partie. +
    Note: ce bouton ne sera pas présent si votre partie est dans les 5 meilleures. + +
  • Rejouer: Appuyez ici pour recommencer une +nouvelle partie solo. + +
  • Retour: + Retourne au menu principal. Équivalent au clavier: ÉCHAP. +
+ +Maintenant que vous savez jouer à Quadra, et que vous êtes même en mesure de faire +des pointages époustouflants, vous avez sûrement envie d'aller affronter quelques autres +personnes! Dirigeons-nous vers le menu multi-joueur pour en apprendre son fonctionnement. + +

Organiser une partie multi-joueur

+Avant de commencer, il faut faire un choix: +

+ + Mode local (pas de réseau) + TCP/IP réseau LAN + TCP/IP réseau Internet + + +
+
    +
  • Mode local (pas de réseau): +Ce bouton sert à créer une partie multi-joueur sur votre ordinateur. Vous pouvez jouer jusqu'à +3 joueurs simultanément (en souhaitant que vous ayez tous des touches de clavier différentes). +Vous n'avez pas besoin d'être connecté en réseau pour utiliser ce mode de jeu.
    +Astuce: on peut aussi utiliser ce mode pour se pratiquer seul. Étant donné que la partie sera +en mode multi-joueur, vous pourrez recommencer aussi souvent que vous le voulez, ce qui est plus pratique +que le mode solo. Par contre, vous ne pourrez pas obtenir votre nom de la liste des meilleurs pointages. + +
  • TCP/IP réseau LAN: +Cliquez sur ce bouton-ci pour jouer une partie en réseau avec d'autres ordinateurs sur un réseau +local. Vous devez avoir le protocole TCP/IP installé dans votre système d'exploitation.
    +Utilisateurs Windows: pour savoir ce protocole est installé dans votre système, +ouvrez les propriétés du réseau du panneau de configuration. +Vous devriez obtenir une fenêtre qui ressemble à celle-ci:
    +

    +Vérifiez que vous avez bien un protocole TCP/IP dans la liste. Celui qui s'appelle "Carte d'accès distant" +sert à vous connecter à l'Internet. Si vous avez une carte réseau dans votre ordinateur, +il est possible de voir un deuxième TCP/IP (ou même davantage). Si vous avez du trouble à +faire fonctionner Quadra dans ce mode multi-joueur, envoyez un courriel à notre support technique: +support@ludusdesign.com + +
  • TCP/IP réseau Internet: +Semblable au mode TCP/IP LAN, ce mode de jeu vous permet de vous connecter avec d'autres ordinateurs en +utilisant le protocole TCP/IP. En plus, ce mode offre la possibilité de créer des parties +dites "publiques" qui pourront être vues à travers le monde entier. +
+ +

Créer une partie multi-joueur

+L'écran de création d'une partie multi-joueur local, LAN ou Internet: +

+ + Nom + Publique Oui/Non + Type de partie + Changement de niveau + Niveau de départ + Lignes pour attaquer + Termine quand + Enregistre la partie + Créer + Enregistrer + Retour + + +
+
    +
  • Nom: +Entrez le nom de la partie.
    +Note: en mode local, cet item n'est pas disponible. + +
  • Publique Oui/Non: +Permet ou non la visibilité de votre partie à travers l'Internet. Quadra "parlera" +avec le serveur de partie publique indiqué dans les options +pour signaler la création de votre partie. Ainsi, les autres personnes qui consulteront la +liste des parties disponibles verront la vôtre. Choisissez "Non" si vous ne voulez pas la rendre +publique. Notez que vos amis pourront tout de même se joindre à votre partie si vous leur +communiquer votre adresse IP. Cela vous permet de faire des parties privées.
    +Note: en mode local et en mode LAN, cet item n'est pas disponible. + +
  • Type de partie: +Il y a 2 types de parties: +
    • Partie libre: Obtenez des points (frags) pour chaque adversaire que vous tuez. Si vous mourez, +vous n'aurez alors qu'à appuyer sur une de vos touches pour recommencer de plus bel.
      + Utilisateurs avancés: pour comprendre le fonctionnement des frags, + cliquez ici. +
    • Dernier survivant: Similaire au mode Partie libre, excepté que vous ne pouvez pas +recommencer immédiatement après avoir été tué. Vous devrez attendre la fin +de la manche, qui se termine lorsqu'un joueur (ou son équipe) devient le dernier survivant. Ensuite, +tous les joueurs seront automatiquement redémarrés pour la manche suivante. +Pendant que vous patientez, profitez-en pour observer vos ennemis ou pour encourager vos coéquipiers qui +sont encore en vie :) +
    + +
  • Changement de niveau: +Détermine si le niveau de difficulté du jeu augmente à chaque fois que vous +avez obtenu 15 lignes. Cette option peut s'avérer utile si vous jouez contre quelqu'un de trop +fort et qui semble invincible (surtout en mode "Partie libre); ainsi, plus il joue longtemps, plus son +niveau de difficulté augmentera, et il finira bien par crever! + +
  • Niveau de départ: +Sélectionnez le niveau de difficulté de départ. Plus vous sélectionnez un +niveau élevé, plus la vitesse de descente des blocs sera rapide. +Conseil: vous devriez laisser le niveau à 1 si vous jouez avec des débutants. + +
  • Lignes pour attaquer: +Le nombre de ligne requis pour attaquer les adversaires. La valeur par défaut étant un +"double". Ce qui signifit que tout ce qui est moins qu'un double (exemple: un simple) n'attaquera +pas les adversaires. Plus vous sélectionnez un nombre élevé, plus il sera difficile +d'attaquer ses adversaires. +
    Conseil: vous devriez laisser le niveau à "double" si vous jouez avec des débutants. +
    Autre conseil: vous pourrez expérimenter un niveau plus élevé si vous voulez jouer +contre plusieurs adversaires de calibre expert. Par exemple, si vous choisissez "5-lignes" comme minimum, vous +vous trouverez alors à "calmer" beaucoup les petits échanges de lignes. + +
  • Termine quand: +Indique à quel moment la partie doit prendre fin. Vous avez le choix parmi: +
    • Jamais: le serveur (créateur) de la partie devra la terminer manuellement. +
    • Après "X" frags: lorsqu'une équipe aura atteint X frags. +
    • Après "X" minutes: un compte à rebours indiquera le temps restant. +
    + +
  • Enregistre la partie: +Si vous désirez enregistrer cette partie pour la conserver. Entrez un nom de fichier dans la +boîte de texte qui apparaîtra.
    +Note: Quadra ajoute automatiquement l'extension .REC aux fichiers de démo. + +
  • Créer: +Créer la partie et vous transporte dans l'écran de jeu multi-joueur. + +
  • Enregistrer: +Sert à conserver ces paramètres de création de partie par défaut. + +
  • Retour: +Retourne au menu précédent. +
+ +

Multi-joueur TCP/IP: Créer ou joindre

+L'écran de connexion pour partie multi-joueur LAN ou Internet: +

+ + Créer + Rafraîchir la liste + Informations d'une partie + Liste des parties + Liste des joueurs d'une partie + Joindre + Connecter à une adresse + Information IP + Retour + + +
+
    +
  • Créer: +Cliquez là-dessus pour créer une nouvelle partie dont vous serez le serveur et l'administrateur. + +
  • Rafraîchir la liste: +Sert à mettre à jour la liste des parties. + +
  • Informations d'une partie: +Donne les renseignements sur la partie sélectionnée dans la liste de gauche. + +
  • Liste des parties: +Ici sont listées les parties présentement disponibles. Cliquez sur le nom d'une partie +pour la sélectionner. + +
  • Liste des joueurs d'une partie: +Affiche la liste des joueurs actuellement présents dans la partie sélectionnée. + +
  • Joindre: +Cliquez ici pour vous joindre à une partie. + +
  • Connecter à une adresse: +Vous pouvez entrer directement une adresse IP ou une adresse URL à laquelle se trouve un serveur +de partie Quadra. +
    Note: en mode TCP/IP Internet, un bouton supplémentaire nommé "Carnet d'adresse" sera +disponible. Ce bouton vous permet d'entrer vos adresses IP les plus fréquemment utilisées. + +
  • Information IP: +Cliquez ici pour afficher votre adresse IP personnelle. Vous pouvez avoir plus d'une adresse selon votre +configuration réseau. + +
  • Retour: +Retourne au menu précédent. +
+ +Lorsque vous entrez dans cet écran, une recherche automatique des parties en cours s'effectue. Si +vous êtes sur le mode TCP/IP Internet, cette recherche à lieu en se connectant au serveur de +partie publique. Celui retourne comme résultat la liste des parties publiques existantes +actuellement. Si vous êtes en mode LAN, la recherche s'effectue à travers le réseau +local en utilisant un signal "broadcast". Il se peut qu'en fonction de votre configuration LAN, +le signal broadcast ne fonctionne pas. Il vous sera alors impossible de voir la liste des parties, mais vous +pourrez tout de même vous connecter à une partie. Il vous suffit d'entrer directement l'adresse +IP de l'ordinateur serveur dans la zone située dans le bas de l'écran. +
En principe, si la recherche a fonctionné et qu'il y a des parties disponibles, vous verrez alors +la liste apparaître. Il vous suffit de cliquer sur le nom d'une partie pour la sélectionner, puis +ensuite cliquez sur le bouton "Joindre la partie" pour vous y joindre. Note pour la souris: +vous pouvez faire un double-clique sur le nom de la partie. +
Note: si la partie que vous avez sélectionnée a été créée par +un serveur Quadra qui n'est pas exactement de la même version que le vôtre, vous ne pourrez pas joindre +celle-ci. Allez sur notre site web pour consulter les plus récentes mises à jour +de Quadra à l'adresse http://www.ludusdesign.com/quadra_fr.html. +
Dans tous les cas, quand vous aurez fini par joindre ou créer une partie, vous arriverez dans +l'écran du jeu multi-joueur, qui sera décrit ci-dessous. + +

L'écran de jeu multi-joueur

+Pour être flexible au maximum, l'écran de jeu multi-joueur est en fait composé de +3 panneaux indépendants. Chacun de ces panneaux peut être fermé à tout +moment en cliquant sur le bouton "Fermer" situé directement au-dessus. Vous pouvez ainsi configurer +l'écran comme bon vous semble, en ouvrant et fermant ces panneaux. Voici le panneau principal: +

+ + Démarrer un joueur + Affiche les joueurs + Information blocs + Information lignes + Choisir le thème + Fenêtre de chat + Fonctions du réseau + Quitte + + +
+
    +
  • Démarrer un joueur: +Cliquer sur un de ces boutons pour démarrer un joueur. +
    Note: si un joueur a déjà été démarré mais que le panneau avait +été fermé, le mot "Démarrer" sera remplacé par "Continuer" pour signaler +que ce joueur est en cours. + +
  • Affiche les joueurs: +Affiche le panneau résumé de la liste des joueurs. C'est aussi dans ce panneau que vous +pourrez observer vos adversaires. + +
  • Information blocs: +Affiche le panneau des statistiques de blocs qui ont été déposés. + +
  • Information lignes: +Similaire au panneau des blocs, mais avec les lignes accomplies par joueur. + +
  • Choisir le thème: +Permet de sélectionner l'image de fond et les effets sonores en fonction du niveau de 1 à 10. +
    Note: pour pouvoir choisir un thème, vous devez l'avoir atteint préalablement +en mode solo! Par exemple, pour pouvoir choisir le thème du niveau 8, vous devez avoir réussi +à vous rendre jusqu'au niveau 8 dans une partie solo. + +
  • Fenêtre de chat: +Ouvre le panneau de dialogue (chat). +
    Note: ce bouton n'est visible qu'en mode multi-joueur TCP/IP (LAN ou Internet) + +
  • Fonctions du réseau: +Affiche le panneau des fonctions relatives au réseau. +
    Note: ce bouton n'est visible qu'en mode multi-joueur TCP/IP (LAN ou Internet) + +
  • Quitte: +Ceci met fin à votre joueur (ou vos joueurs) et vous transporte dans l'écran des résultats. +Si la partie ne contenait aucun joueur, ce bouton vous ramène au menu précédent. Notez +que si vous êtes serveur, ceci ne termine pas la partie: les autres ordinateurs pourront +continuer de jouer, tant que vous ne signalerez pas la fin de partie (voir le menu des résultats). +
    Note: ce bouton n'est visible que lorsque le panneau de droite est fermé. +
+ +Dès que vous entrez dans l'écran de jeu multi-joueur, vous pouvez commencer à manipuler +les panneaux à votre guise. Si la partie vient tout juste d'être créer, vous apercevrez +au centre de l'écran le mot "Pause" ressemblant un peu à ceci: +
+ +
+
Ce dernier indique que la partie est temporairement suspendue, ou encore quelle n'est pas commencée. +Seul le serveur peut signaler le départ en appuyant sur la touche PAUSE. Un +décompte de 5 secondes s'en suivra, puis la partie débutera pour de bon. Si vous voulez +temporairement suspendre la partie, vous n'avez qu'à appuyer sur PAUSE. Par la suite, aucun autre joueur ne pourra +enlever la fonction pause à part le serveur et vous-même. Autrement dit, si le serveur décide +de mettre pause, personne d'autre ne peut l'enlever. +
Note: le jeu débute sur PAUSE en mode multi-joueur TCP/IP (LAN ou Internet) seulement. + +

Multi-joueur: démarrer un joueur

+

+ + Démarrer + Choisir l'équipe + Joueurs dans l'équipe + + +
+
    +
  • Démarrer: +Démarre ce joueur dans l'équipe indiquée par la couleur sélectionnée. +Ce bouton demande la permission au serveur pour démarrer un nouveau joueur. Il se peut que le +serveur rejette votre demande. Voici les raisons possibles: +
      +
    • Le serveur refuse les joueurs: Le créateur de partie refuse les nouveaux joueurs. Peut être +que vous devriez tenter de dialoguer avec lui dans la fenêtre de chat pour demander l'autorisation. +
    • Nom déjà utilisé: Un joueur utilisant exactement le même nom que vous existe +déjà dans la partie. Vous devez changer de nom pour pouvoir joindre cette partie, ou +demander à l'administrateur d'éliminer le joueur pour vous laisser la place. +
    • La partie est terminée: Si cette partie est déjà terminée, vous ne pouvez +plus joindre de joueur. +
    + +
  • Choisir l'équipe: +Cliquez ici pour changer d'équipe avant de vous joindre à la partie. Vous avez l'opportunité +de constater quels joueurs sont déjà dans la partie, ou encore quelle équipe semble le plus avoir +besoin de votre coup de main. + +
  • Joueurs dans l'équipe: +Cette liste illustre les joueurs déjà présents dans l'équipe +que vous avez présentement sélectionnée. +
+ +Particularité: si vous étiez déjà dans une partie et que, pour une raison ou pour +une autre, votre connexion a été interrompue, vous pouvez revenir dans la partie. Votre nom +devrait être déjà dans la liste des joueurs (à moins que le serveur n'aie décidé +de vous éliminer). Pour continuer en conservant vos statistiques, il s'agit de re-joindre la même +partie en utilisant la même couleur d'équipe que vous utilisiez déjà. Sinon, +Quadra considère que vous êtes un nouveau joueur, et va remettre toutes les statistiques à zéro. +
Toutefois, au moment où vous aviez sorti (volontairement ou involontairement), vous allez être +considéré comme quelqu'un de mort. En mode dernier survivant, cela pourrait avoir pour effet de +terminer une manche, considérant que vous étiez le seul adversaire encore vivant. + +

Multi-joueur: un joueur en cours de partie

+Ce panneau devrait vous sembler familier si vous avez lu la section sur le mode solo: +

+ + Blocs suivants + Ferme + Canevas de jeu + Lignes en attente + Vos informations + + +
+
    +
  • Blocs suivants: +Cette zone illustre les 3 blocs suivant celui que vous contrôler présentement. + +
  • Ferme: +Ce bouton ferme le panneau et retourne au panneau principal. + +
  • Canevas de jeu: +Cette dans cette région que vous déposez allègrement vos blocs. + +
  • Lignes en attente: +Cette zone indique combien de lignes vous allez recevoir de vos adversaires. Plus la colonne devient +haute, plus vous recevrez de lignes lorsque vous déposerez votre prochain bloc. Utilisez cette +information judicieusement, votre survie en dépendra! + +
  • Vos informations: +Dans le bas de l'écran, vous verrez vos informations courantes: +
      +
    • Score: Indique votre pointage actuel. +
    • Lignes: Un premier champ indique le nombre de ligne accomplie au cours de cette vie, tandis que l'autre +indique le nombre cumulatif de ligne accompli au cours de la partie. +
    • Frags: Indique le nombre d'adversaire que vous avez tué sans pitié. Pour +tuer quelqu'un, il faut lui envoyer des lignes et provoquer son décès. Pour savoir exactement +comment fonctionne les frags, consultez le guide de stratégie multi-joueur. +
    • Morts: Indique le nombre de fois que vous êtes mort. +
    • Niveau: Votre niveau de difficulté. +
    +
+ +

Multi-joueur: afficher les joueurs

+

+ + Liste des joueurs + Affiche quoi + + +
+
    +
  • Liste des joueurs: +Liste des joueurs actifs dans la partie présentement. Les joueurs provenant des autres ordinateurs +connectés à cette partie seront inscrits dans un bouton. Vous pouvez cliquer sur le nom d'un +de ces joueurs, puis ensuite cliquer sur le bouton "Observe sélectionnés" pour les voir en pleine action. Si vous +sélectionnez seulement 1 joueur, il sera affiché en pleine grandeur dans le panneau. Si vous +en sélectionnez 2,3 ou 4, ils seront affichés en format réduit. +
    Note: vous ne pouvez pas observer un joueur qui a été déconnecté ou qui +a quitté la partie. + +
  • Affiche quoi: +Cliquez ici pour changer quel renseignement sera affiché à droite de chaque nom de joueur. +Vous pouvez choisir parmi: Frags & Morts, Score, Blocs, et Lignes. +
+ +

Multi-joueur: affiche les statistiques de blocs

+

+ + Totaux par bloc + Joueur vs joueur + + +
+
    +
  • Totaux par bloc: +Indique le nombre de bloc de chaque type qui ont été déposé au cours de la +partie, ainsi que le total de tous les blocs. Vous pouvez comparer la vitesse d'un joueur par rapport à +un autre en regardant lequel a déposé le plus de bloc. Toutefois, il est possible que les deux +joueurs n'aient pas commencé à jouer au même moment. + +
  • Joueur vs joueur: +Cliquez sur le nom d'un joueur pour passer au joueur suivant. +
+ +

Multi-joueur: affiche les statistiques de lignes

+

+ + Totaux des lignes + Joueur vs joueur + + +
+
    +
  • Totaux des lignes: +Indique le nombre de combo de ligne ont été exécuté par chaque joueur. +Le total des lignes est aussi indiqué dans le bas. + +
  • Joueur vs joueur: +Cliquez sur le nom d'un joueur pour passer au joueur suivant. +
+ +

Multi-joueur: fenêtre de dialogue (chat)

+

+ + Zone de dialogue + Zone d'entrée + De qui + A qui + + +
+
    +
  • Zone de dialogue: +Affiche le dialogue qui a eu lieu. Les lignes de textes provenant des autres joueurs seront en +fonction de la couleur de leurs équipes. + +
  • Zone d'entrée: +Cliquez dans cette zone pour entrer une ligne de texte. Si vous êtes en pleine partie, tâchez +de taper rapidement car vous ne pourrez plus contrôler votre joueur pendant que vous entrez votre message! +
    Raccourci au clavier: Appuyez sur RETOUR pour commencer à taper, puis RETOUR +une deuxième fois pour l'émettre. Vous pouvez faire ECHAP pour annuler la ligne de texte en cours. + +
  • De qui: +Cette zone vous permet de modifier la provenance de vos messages. Étant donné que vous +pouvez configurer jusqu'à 3 joueurs par ordinateur, vous pouvez décider de "qui" proviennent +les messages que les autres ordinateurs recevront de vous. + +
  • A qui: +Détermine à qui vous désirez émettre vos messages. Vous pouvez choisir TOUS +pour l'envoyer à tous les autres joueurs, ou choisir une couleur d'équipe pour l'envoyer +uniquement à cette équipe. +
+ +

Multi-joueur: les fonctions réseau

+

+ + Ferme joueur + Ferme connexion + Accepte joueur + Accepte connexion + Tester Ping + M.A.J. des joueurs observés + Information IP + + +
+
    +
  • Ferme joueur: +Affiche la liste des joueurs présents dans la partie, et permet de les éliminer. +
    Note: ce bouton n'est visible que sur l'ordinateur serveur. + +
  • Ferme connexion: +Affiche la liste des connexions réseaux en cours, et vous permet de les éliminer. +Si vous fermez une connexion, vous vous trouverez en même temps à éliminer les joueurs +qui étaient sur cet ordinateur. +
    Note: ce bouton n'est visible que sur l'ordinateur serveur. + +
  • Accepte joueur: +Permet d'empêcher les nouveaux joueurs de s'ajouter à votre partie. Tant que cette option sera +à "Non", les joueurs qui tenteront de se joindre recevront un message disant "Le serveur a refusé +votre demande". Néanmoins, ils pourront dialoguer avec vous grâce à la fenêtre de chat. +
    Note: cette option n'est visible que sur l'ordinateur serveur. + +
  • Accepte connexion: +Permet d'empêcher toute tentative de connexion à votre partie. Utilisez cette fonction pour +assurer votre sécurité au cas où des personnes malfaisantes tenteraient de vous +nuire. Prenez garde toutefois que personne ne pourra se connecter pendant que cette option est +à "Non". +
    Note: cette option n'est visible que sur l'ordinateur serveur. + +
  • Tester Ping: +Cliquez sur ce bouton pour connaître le temps réponse entre votre ordinateur et le serveur. +Si vous avez un temps dépassant 2000 millisecondes, votre connexion est lente, et tous les +événements du jeu risquent d'arriver en retard. +Si vous êtes vous-même le serveur, ce temps devrait être 10 millisecondes environ. + +
  • M.A.J. des joueurs observés: +Permet de décider la fréquence des mises à jour visuelles des joueurs observés. +Par exemple, si vous indiquez 10 et que vous observez votre adversaire dans un autre panneau, Quadra +transmettra 10 mises à jour par seconde afin que vous puissiez suivre ses déplacements. +Si votre connexion est lente, vous pouvez mettre 0 pour complètement désactiver les mises à +jour. Vous pourrez tout de même observer les autres joueurs, cependant, leurs blocs vont sembler +"apparaître" directement au lieu de se déplacer. Si vous êtes sur un réseau local +à grande vitesse, vous pouvez tenter de mettre une valeur plus élevée pour obtenir +des déplacements beaucoup plus fluides. Prenez notes des points suivants: +
      +
    • Le changement de vitesse de mise à jour s'effectue en temps réel. Cependant, si vous +voulez constater les changements de vitesses demandés par les autres joueurs, +vous devrez fermer puis rouvrir ces joueurs si vous les observiez déjà. +
    • A titre d'indication, sur Internet avec un modem 56.6Kps, vous devriez tenir ce nombre autour de 10 ou +moins. Sur un LAN, autour de 30 devrait être acceptable et assez fluide. Faites vos expérimentations! +
    • Il se peut que malgré que vous ayez demandé 50 mises à jour, un joueur en +particulier semble rester saccadé ou immobile. La raison est que cet ordinateur a peut-être +spécifié une vitesse de rafraîchissement d'environ 0, et que Quadra va s'ajuster selon +la vitesse la plus basse des 2 ordinateurs. +
    • Si vous observez des joueurs en format réduit (c'est à dire plusieurs joueurs dans le même +panneau), Quadra va automatiquement diminuer de moitié la vitesse de rafraîchissement, étant +donné la petite dimension des ces canevas. +
    + +
  • Information IP: +Indique votre adresse IP actuelle. + +

    Multi-joueur: l'écran des résultats

    +

    + + Titre des colonnes + Totaux par joueur + Signaler fin de partie + Changer de page + Retour + Zone de dialogue (chat) + + +
    +
      +
    • Titre des colonnes: +Cliquez sur le titre d'une colonne pour sélectionner l'ordre de tri des joueurs. + +
    • Totaux par joueur: +Cette région indique les divers totaux par joueurs avec des totaux cumulatifs par équipe. + +
    • Signaler fin de partie: +Ce bouton sert à signaler la fin de la partie à tous les joueurs actifs présentement. +Une fois la fin signalée, personne ne pourra se joindre à cette partie, car elle sera considérée +comme terminée. Si, lors de la création de cette partie, vous aviez choisi qu'elle se termine +au bout de X minutes ou lorsqu'une équipe atteint X frags, vous pouvez décider +de mettre fin avant l'échéance. Par ailleurs, ce bouton disparaîtra si la partie est +déjà terminée. +
      Note: ce bouton n'est visible que sur l'ordinateur serveur ET en mode multi-joueur TCP/IP + +
    • Changer de page: +Cliquez ici pour changer de page de statistiques. + +
    • Retour: +Ce bouton vous retourne au menu précédent et vous déconnecte de la partie. Si vous +étiez le serveur, ceci à pour effet d'émettre un message "Le serveur a été +déconnecté" aux autres ordinateurs, et cela interrompt leurs parties s'ils étaient +encore actifs. +
      Note: Les autres joueurs ne pourront plus dialoguer entre eux si vous sortez de l'écran des +résultats, car tous les messages doivent absolument passer par le serveur. + +
    • Zone de dialogue (chat): +Cette zone est exactement identique au panneau "fenêtre de chat" de l'écran de jeu. Vous +pouvez ainsi continuer à dialoguer avec les autres joueurs malgré que la partie soit terminée. +
      Note: ce panneau n'est visible qu'en mode multi-joueur TCP/IP (LAN ou Internet) +
    + +

    La centrale de démo

    +

    + +Répertoire en cours +Information +Liste des fichiers +Visionner démo +Retour + + +
    +
      +
    • Répertoire en cours: +Vous pouvez taper un répertoire différent de celui proposé par défaut (qui se +trouve à être le répertoire où est installé Quadra). + +
    • Information: Cette zone indique les renseignements +sur la partie sélectionnée dans la liste de gauche, ainsi que la liste des joueurs et leurs +statistiques lors de la fin de partie. + +
    • Liste des fichiers: Cette liste affiche tous les +fichiers *.REC trouvés dans le répertoire en cours. Cliquez sur un fichier pour le +sélectionner, ou double-cliquez pour passez directement au visionnement.
      +Note: vous remarquerez que les fichiers de démo appelés GLOBALx.REC et LOCALx.REC +correspondent aux démos des meilleurs pointages mondiaux et locaux. + +
    • Visionner démo: Ce bouton vous +transporte dans l'écran de jeu afin de regarder se dérouler la partie que vous avez +choisie. Des boutons de contrôle Vitesse lente et Vitesse rapide seront disponible +lors du déroulement de la partie. + +
    • Retour: +Retourne au menu principal. +
    + +
    +support@ludusdesign.com
    +
    ©1999 Ludus Design. Tous droits réservés.
    +
    Dernière mise à jour: 1999-08-23
    +
+ + diff --git a/help/hfond.gif b/help/hfond.gif new file mode 100644 index 0000000..7af3730 Binary files /dev/null and b/help/hfond.gif differ diff --git a/help/htext.gif b/help/htext.gif new file mode 100644 index 0000000..fecb5d8 Binary files /dev/null and b/help/htext.gif differ diff --git a/help/icon_reseau.gif b/help/icon_reseau.gif new file mode 100644 index 0000000..f8036e7 Binary files /dev/null and b/help/icon_reseau.gif differ diff --git a/help/keyb.gif b/help/keyb.gif new file mode 100644 index 0000000..0ad9867 Binary files /dev/null and b/help/keyb.gif differ diff --git a/help/menuprin.jpg b/help/menuprin.jpg new file mode 100644 index 0000000..77c2782 Binary files /dev/null and b/help/menuprin.jpg differ diff --git a/help/menuprin_eng.jpg b/help/menuprin_eng.jpg new file mode 100644 index 0000000..d6ec91c Binary files /dev/null and b/help/menuprin_eng.jpg differ diff --git a/help/multi.gif b/help/multi.gif new file mode 100644 index 0000000..1e5a8e5 Binary files /dev/null and b/help/multi.gif differ diff --git a/help/multi.jpg b/help/multi.jpg new file mode 100644 index 0000000..d40072c Binary files /dev/null and b/help/multi.jpg differ diff --git a/help/ombre.gif b/help/ombre.gif new file mode 100644 index 0000000..2eb1f59 Binary files /dev/null and b/help/ombre.gif differ diff --git a/help/option.jpg b/help/option.jpg new file mode 100644 index 0000000..12ecc8c Binary files /dev/null and b/help/option.jpg differ diff --git a/help/pane.gif b/help/pane.gif new file mode 100644 index 0000000..fd2f572 Binary files /dev/null and b/help/pane.gif differ diff --git a/help/panebloc.gif b/help/panebloc.gif new file mode 100644 index 0000000..9b6f194 Binary files /dev/null and b/help/panebloc.gif differ diff --git a/help/panechat.gif b/help/panechat.gif new file mode 100644 index 0000000..53299cb Binary files /dev/null and b/help/panechat.gif differ diff --git a/help/paneline.gif b/help/paneline.gif new file mode 100644 index 0000000..68510f0 Binary files /dev/null and b/help/paneline.gif differ diff --git a/help/panenet.gif b/help/panenet.gif new file mode 100644 index 0000000..2d1db30 Binary files /dev/null and b/help/panenet.gif differ diff --git a/help/paneplay.gif b/help/paneplay.gif new file mode 100644 index 0000000..38ff0aa Binary files /dev/null and b/help/paneplay.gif differ diff --git a/help/paneshow.gif b/help/paneshow.gif new file mode 100644 index 0000000..1a39bc8 Binary files /dev/null and b/help/paneshow.gif differ diff --git a/help/panestar.gif b/help/panestar.gif new file mode 100644 index 0000000..b4d634a Binary files /dev/null and b/help/panestar.gif differ diff --git a/help/pause.gif b/help/pause.gif new file mode 100644 index 0000000..b2ff4e7 Binary files /dev/null and b/help/pause.gif differ diff --git a/help/pointage.jpg b/help/pointage.jpg new file mode 100644 index 0000000..3e76e46 Binary files /dev/null and b/help/pointage.jpg differ diff --git a/help/reso.gif b/help/reso.gif new file mode 100644 index 0000000..d1e5275 Binary files /dev/null and b/help/reso.gif differ diff --git a/help/result.jpg b/help/result.jpg new file mode 100644 index 0000000..0e1fe60 Binary files /dev/null and b/help/result.jpg differ diff --git a/help/solo.gif b/help/solo.gif new file mode 100644 index 0000000..6f5983c Binary files /dev/null and b/help/solo.gif differ diff --git a/help/solo2.jpg b/help/solo2.jpg new file mode 100644 index 0000000..80468bb Binary files /dev/null and b/help/solo2.jpg differ diff --git a/help/souris.gif b/help/souris.gif new file mode 100644 index 0000000..5534ece Binary files /dev/null and b/help/souris.gif differ diff --git a/help/th1s.gif b/help/th1s.gif new file mode 100644 index 0000000..cc4d8f0 Binary files /dev/null and b/help/th1s.gif differ diff --git a/help/th2s.gif b/help/th2s.gif new file mode 100644 index 0000000..1a2aaa7 Binary files /dev/null and b/help/th2s.gif differ diff --git a/images/black.pcx b/images/black.pcx new file mode 100644 index 0000000..8b6fd86 Binary files /dev/null and b/images/black.pcx differ diff --git a/images/bloctemp.pcx b/images/bloctemp.pcx new file mode 100644 index 0000000..3ee69d8 Binary files /dev/null and b/images/bloctemp.pcx differ diff --git a/images/cursor.raw b/images/cursor.raw new file mode 100644 index 0000000..76936f1 Binary files /dev/null and b/images/cursor.raw differ diff --git a/images/debut0.raw b/images/debut0.raw new file mode 100644 index 0000000..e8cda86 Binary files /dev/null and b/images/debut0.raw differ diff --git a/images/debut0f.raw b/images/debut0f.raw new file mode 100644 index 0000000..98e94a0 Binary files /dev/null and b/images/debut0f.raw differ diff --git a/images/debut1.raw b/images/debut1.raw new file mode 100644 index 0000000..218cc89 Binary files /dev/null and b/images/debut1.raw differ diff --git a/images/debut1f.raw b/images/debut1f.raw new file mode 100644 index 0000000..84cf198 Binary files /dev/null and b/images/debut1f.raw differ diff --git a/images/debut2.raw b/images/debut2.raw new file mode 100644 index 0000000..36a564e Binary files /dev/null and b/images/debut2.raw differ diff --git a/images/debut2f.raw b/images/debut2f.raw new file mode 100644 index 0000000..2f690c5 Binary files /dev/null and b/images/debut2f.raw differ diff --git a/images/debut3.raw b/images/debut3.raw new file mode 100644 index 0000000..42730fe Binary files /dev/null and b/images/debut3.raw differ diff --git a/images/debut3f.raw b/images/debut3f.raw new file mode 100644 index 0000000..b4c036d Binary files /dev/null and b/images/debut3f.raw differ diff --git a/images/debut4.raw b/images/debut4.raw new file mode 100644 index 0000000..d1be054 Binary files /dev/null and b/images/debut4.raw differ diff --git a/images/debut4f.raw b/images/debut4f.raw new file mode 100644 index 0000000..b127bf5 Binary files /dev/null and b/images/debut4f.raw differ diff --git a/images/debut5.raw b/images/debut5.raw new file mode 100644 index 0000000..4558316 Binary files /dev/null and b/images/debut5.raw differ diff --git a/images/debut5f.raw b/images/debut5f.raw new file mode 100644 index 0000000..b44d624 Binary files /dev/null and b/images/debut5f.raw differ diff --git a/images/debut6.raw b/images/debut6.raw new file mode 100644 index 0000000..7b35215 Binary files /dev/null and b/images/debut6.raw differ diff --git a/images/debut6f.raw b/images/debut6f.raw new file mode 100644 index 0000000..f3661fb Binary files /dev/null and b/images/debut6f.raw differ diff --git a/images/debut7.raw b/images/debut7.raw new file mode 100644 index 0000000..1020162 Binary files /dev/null and b/images/debut7.raw differ diff --git a/images/debut7f.raw b/images/debut7f.raw new file mode 100644 index 0000000..f89bcfa Binary files /dev/null and b/images/debut7f.raw differ diff --git a/images/debut8.raw b/images/debut8.raw new file mode 100644 index 0000000..8f6e19f Binary files /dev/null and b/images/debut8.raw differ diff --git a/images/debutnr.raw b/images/debutnr.raw new file mode 100644 index 0000000..05f6277 Binary files /dev/null and b/images/debutnr.raw differ diff --git a/images/debuto.raw b/images/debuto.raw new file mode 100644 index 0000000..1b52c5c Binary files /dev/null and b/images/debuto.raw differ diff --git a/images/debutof.raw b/images/debutof.raw new file mode 100644 index 0000000..ac9905c Binary files /dev/null and b/images/debutof.raw differ diff --git a/images/fond0.pcx b/images/fond0.pcx new file mode 100644 index 0000000..31e7740 Binary files /dev/null and b/images/fond0.pcx differ diff --git a/images/fond1.pcx b/images/fond1.pcx new file mode 100644 index 0000000..d166eda Binary files /dev/null and b/images/fond1.pcx differ diff --git a/images/fond2.pcx b/images/fond2.pcx new file mode 100644 index 0000000..430ea4e Binary files /dev/null and b/images/fond2.pcx differ diff --git a/images/fond3.pcx b/images/fond3.pcx new file mode 100644 index 0000000..9a069b5 Binary files /dev/null and b/images/fond3.pcx differ diff --git a/images/fond4.pcx b/images/fond4.pcx new file mode 100644 index 0000000..cb66162 Binary files /dev/null and b/images/fond4.pcx differ diff --git a/images/fond5.pcx b/images/fond5.pcx new file mode 100644 index 0000000..13c4ef9 Binary files /dev/null and b/images/fond5.pcx differ diff --git a/images/fond6.pcx b/images/fond6.pcx new file mode 100644 index 0000000..52bc8c6 Binary files /dev/null and b/images/fond6.pcx differ diff --git a/images/fond7.pcx b/images/fond7.pcx new file mode 100644 index 0000000..5438b35 Binary files /dev/null and b/images/fond7.pcx differ diff --git a/images/fond8.pcx b/images/fond8.pcx new file mode 100644 index 0000000..ff90788 Binary files /dev/null and b/images/fond8.pcx differ diff --git a/images/fond9.pcx b/images/fond9.pcx new file mode 100644 index 0000000..8901c42 Binary files /dev/null and b/images/fond9.pcx differ diff --git a/images/game_1.raw b/images/game_1.raw new file mode 100644 index 0000000..c1164c5 Binary files /dev/null and b/images/game_1.raw differ diff --git a/images/game_2.raw b/images/game_2.raw new file mode 100644 index 0000000..9a24214 Binary files /dev/null and b/images/game_2.raw differ diff --git a/images/game_3.raw b/images/game_3.raw new file mode 100644 index 0000000..b3a48d3 Binary files /dev/null and b/images/game_3.raw differ diff --git a/images/game_4.raw b/images/game_4.raw new file mode 100644 index 0000000..ce1768d Binary files /dev/null and b/images/game_4.raw differ diff --git a/images/game_5.raw b/images/game_5.raw new file mode 100644 index 0000000..b071f5e Binary files /dev/null and b/images/game_5.raw differ diff --git a/images/gamelvup.raw b/images/gamelvup.raw new file mode 100644 index 0000000..73a131b Binary files /dev/null and b/images/gamelvup.raw differ diff --git a/images/gamepaus.raw b/images/gamepaus.raw new file mode 100644 index 0000000..e20d20d Binary files /dev/null and b/images/gamepaus.raw differ diff --git a/images/guy.pcx b/images/guy.pcx new file mode 100644 index 0000000..c99af62 Binary files /dev/null and b/images/guy.pcx differ diff --git a/images/hscor1fs.raw b/images/hscor1fs.raw new file mode 100644 index 0000000..f110259 Binary files /dev/null and b/images/hscor1fs.raw differ diff --git a/images/hscor2fs.raw b/images/hscor2fs.raw new file mode 100644 index 0000000..e3349d9 Binary files /dev/null and b/images/hscor2fs.raw differ diff --git a/images/hscore.raw b/images/hscore.raw new file mode 100644 index 0000000..ea7e419 Binary files /dev/null and b/images/hscore.raw differ diff --git a/images/hscore1.raw b/images/hscore1.raw new file mode 100644 index 0000000..fb5317a Binary files /dev/null and b/images/hscore1.raw differ diff --git a/images/hscore1f.raw b/images/hscore1f.raw new file mode 100644 index 0000000..9b3c1ea Binary files /dev/null and b/images/hscore1f.raw differ diff --git a/images/hscore1s.raw b/images/hscore1s.raw new file mode 100644 index 0000000..aedbe26 Binary files /dev/null and b/images/hscore1s.raw differ diff --git a/images/hscore2.raw b/images/hscore2.raw new file mode 100644 index 0000000..45c0934 Binary files /dev/null and b/images/hscore2.raw differ diff --git a/images/hscore2f.raw b/images/hscore2f.raw new file mode 100644 index 0000000..13aa36e Binary files /dev/null and b/images/hscore2f.raw differ diff --git a/images/hscore2s.raw b/images/hscore2s.raw new file mode 100644 index 0000000..8fb0103 Binary files /dev/null and b/images/hscore2s.raw differ diff --git a/images/hscoretf.raw b/images/hscoretf.raw new file mode 100644 index 0000000..11dd965 Binary files /dev/null and b/images/hscoretf.raw differ diff --git a/images/multi.raw b/images/multi.raw new file mode 100644 index 0000000..73e73cd Binary files /dev/null and b/images/multi.raw differ diff --git a/images/quadra.xpm b/images/quadra.xpm new file mode 100644 index 0000000..ed5ab82 --- /dev/null +++ b/images/quadra.xpm @@ -0,0 +1,123 @@ +/* XPM */ +static char * quadra_xpm[] = { +"48 48 72 1", +" c None", +". c #FFFF00", +"+ c #808000", +"@ c #FF0000", +"# c #800000", +"$ c #FFFFFF", +"% c #FCFCFC", +"& c #F8F8F8", +"* c #F5F5F5", +"= c #F0F0F0", +"- c #EDEDED", +"; c #E9E9E9", +"> c #E6E6E6", +", c #E5E5E5", +"' c #E1E1E1", +") c #DEDEDE", +"! c #DDDDDD", +"~ c #D9D9D9", +"{ c #D6D6D6", +"] c #CECECE", +"^ c #CBCBCB", +"/ c #C7C7C7", +"( c #C3C3C3", +"_ c #C0C0C0", +": c #BFBFBF", +"< c #BDBDBD", +"[ c #B5B5B5", +"} c #B1B1B1", +"| c #AEAEAE", +"1 c #ADADAD", +"2 c #AAAAAA", +"3 c #A9A9A9", +"4 c #A6A6A6", +"5 c #A2A2A2", +"6 c #9F9F9F", +"7 c #9A9A9A", +"8 c #979797", +"9 c #969696", +"0 c #939393", +"a c #909090", +"b c #8F8F8F", +"c c #8C8C8C", +"d c #888888", +"e c #878787", +"f c #848484", +"g c #808080", +"h c #797979", +"i c #767676", +"j c #757575", +"k c #717171", +"l c #6F6F6F", +"m c #6E6E6E", +"n c #676767", +"o c #626262", +"p c #5F5F5F", +"q c #5B5B5B", +"r c #545454", +"s c #505050", +"t c #4D4D4D", +"u c #414141", +"v c #404040", +"w c #3E3E3E", +"x c #3D3D3D", +"y c #393939", +"z c #363636", +"A c #323232", +"B c #2F2F2F", +"C c #2A2A2A", +"D c #272727", +"E c #232323", +"F c #202020", +"G c #000000", +" FFFFFFF ", +" FFErkoAFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFF ", +" FFm;$'*%5CFF$kFFk$r8EFF F$$)6AFF($;bEFgmFF ", +"FFg$7AFEn;^DF$kFFk$r$_CFF F$6f!;uF(_h-|Fa$0FF ", +"Fx%cFFFFFu*bF$kFFk$r$;BF ", +"F:[FFFFFFFq$y$kFFk$r$wF//FF$kFFF^4(<8*}Fa'FB=bFF", +"F9,EFDsCFF8;F$kFFk$r$^(!$sF$kFFz&i([;}EFa&((-=DF", +"Fx%cFw$%3r=bF$iFFi$r$6aa)3F$kFE<'D(|d-yFa=aaa&lF", +"FFg$7ADo-$^DF]/CC/]r$wFFd'F$0h~;uF(|E^)ta'FFF_1F", +" FFm;$'*%-:FFu;=*;ur$wFFr$A$$)6AFF(|FC2'a'F Fc{F", +" FFErkozp$vFFCnnCFFFFGGGGGGFFFFFFFFFFFxFFF FFFF", +" FFFFFFD$mFFFFFFFGGGG@@@#@G FFF ", +" FFFFF G$$@@@@#G ", +" G@$@@@@@#G ", +" G@@@@#@##G ", +" GGG@@@@@###G ", +" GGGG$@$@@@@#@##G ", +" GG$@@$@@@@@@@@@@##G ", +" G$@$@@@@#@@#@####GG ", +" GG$@@@@@@####GGGGG ", +" GG$@@@@@##GGGGGGGG ", +" GG$@@@@@#GGGGG ", +" GG@@@@@@##G ", +" GG@@@@#@#G ", +" GG@@#@####G ", +" GG@#####GG ", +" GG###GGGG ", +" GGGG GGGGGGG ", +" GGG$$$G GGGGGGG ", +" G$$$$$.+G GGG GGGG ", +" GG$....+G GGGGGG$.+G ", +" G.$....+G GGGGGG.$.$....++G ", +" GG.....+GGG$$.$..........++G ", +" GG$.....+................+.+G ", +" GG..................+..+..++G ", +" GGG$..........+.+...++++++GGGG ", +" GG....+..+..+++++++GGGGGGGGGG ", +" GG.$.+++++++GGGGGGGGGGGGGGGGG ", +" GGG+++GGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGG ", +" ", +" ", +" ", +" ", +" "}; diff --git a/images/quadra2.xpm b/images/quadra2.xpm new file mode 100644 index 0000000..c985d6e --- /dev/null +++ b/images/quadra2.xpm @@ -0,0 +1,116 @@ +/* XPM */ +static char * quadra2_xpm[] = { +"48 48 65 1", +" c None", +". c #020204", +"+ c #828204", +"@ c #840204", +"# c #484345", +"$ c #4D0204", +"% c #884544", +"& c #8B6264", +"* c #636365", +"= c #B15554", +"- c #684446", +"; c #462624", +"> c #282324", +", c #885554", +"' c #683234", +") c #C4C4C5", +"! c #685655", +"~ c #2B1514", +"{ c #4D3234", +"] c #9C5554", +"^ c #393739", +"/ c #595454", +"( c #C15C5C", +"_ c #785655", +": c #1A1517", +"< c #9E4D4C", +"[ c #A5A4A5", +"} c #D70204", +"| c #FEFE04", +"1 c #552A2B", +"2 c #8C5D5C", +"3 c #653B3C", +"4 c #17090B", +"5 c #827E7C", +"6 c #823E3C", +"7 c #E6E6E4", +"8 c #868687", +"9 c #421E1C", +"0 c #7A7A79", +"a c #DCDCDA", +"b c #B9B9B9", +"c c #A20204", +"d c #FDFDFB", +"e c #979799", +"f c #6C0204", +"g c #AE6264", +"h c #FD0204", +"i c #5A4847", +"j c #3A2626", +"k c #3A1E1C", +"l c #593636", +"m c #7C6264", +"n c #784848", +"o c #7A3E3C", +"p c #864C4C", +"q c #B35E5C", +"r c #A05C5C", +"s c #BE0204", +"t c #F0F0F0", +"u c #0C0B0C", +"v c #492B2C", +"w c #2E2E2C", +"x c #2C1E1C", +"y c #483B3C", +"z c #5D5D5C", +"5[5*z////!//!/!!/!!!!!!!!!__!___________,,_nn-lv", +"[db0/#^^//!z!/!/!!!!!!!!!!!!__!__n____,____nn-lv", +"5)0w.^/#u:#/y:>i/>ux^i____y...xi_i4..x-n{ln,-l{j", +"**u/atatteu#.dzy^zt^8.#mmm.dta8:{4bda0.>*/.nnlvj", +"/>*d8:../7)..dzwjzd^dbuim2.de*a7>.bbzt[.5d8.3{;k", +"#:70.w^^uxt5.dz>jzdydab.im.dz..)a.b[.ydw5tt0uvvx", +":0).w^###.87.dz>>zd^d#t84_.dz:x:d*b[..dz5azty:;x", +".ee.y#i/!>#d:dzx>zdyd>*t^{.dzx{.)eb[./d^5a.ebukx", +"u[e.#ii!!^#dxdzxwzd^d>.)b..dzx-.)ebb8t[.5a.:)yuk", +":0).^w^uu.87.dzx>zd^d)bad^.dzx{xt*b[7[..5tbbb5.~", +"y:a0.>dde^t0.d*::*d^de05ae.dz..baub[0t>.5t00*8>4", +"/:*t8:./td)u.)buu))^d>..0a.d5za7>.b[.)aw5a.u.*#.", +"/#u/7datd7b.x>77t7>#d>.$^d:dda8:xub[.ue75a.v:#z.", +"zi^>.^z#:/d>w>u//u>:.#ec$44...4jlv::jju>.4j3;4u4", +"!/iiy>xx:udzjiyj>j^v.cb}}sc@$.jl-nno---ll33nn;k~", +"!/z!/i##y>.:y!!iii--y.}}h}f}$f.3n,,,,,,pnpp]nljx", +"!!!!m!!/!-i-i___n!n!...hhh}hfff.n,]rrrrrrrrrplvk", +"!!!mmmmmmm_!__m2....dhdhhhh@h@@.n,]&grgrrqrgp';k", +"!z!mmmmmmmmmm2..dhhdhhhhhhhhhh@@.n,rggggggrqplv9", +"!!!mmmm2mm22m2.dhdhhhh@hh@h@@@@..np22ggrgggr<';k", +"!!!mm_mm&mm&2_..dhhhhhh@@@@.....voprgrggrggr watchers; + void add_watcher(Watcher *w); + void remove_watcher(Net_connection *nc); + //Potato stuff (server only) + Byte potato_team_on_last_stamp; + Dword potato_lines; + Zone *z_lines, *z_potatolines, *z_linestot, *z_potatolinestot; + int team_potato_lines, team_potato_linestot; + bool should_remove_bonus; //Remove bonus when we have the chance + + Dword gone_time; + /* valeur possible de canvas->idle: + 0: busy, canvas non disponible (en recursion) + 1: idle, fait rien (en mouvement de bloc) + 2: mort, en attente + 3: gone, le joueur a quitté le jeu + */ + int idle; + State state; + bool dying; //Set to true while in Player_dead or Player_firstfrag + bool wait_download; //Set when waiting for a P_DOWNLOAD (clients only) + bool trying_to_drop; + Word watch_date; + bool small_watch; + Palette *pal; + Bitmap *bit; + Random rnd; + Byte color; + Net_connection *remote_adr; + bool local_player; + bool occupied[36][18]; + Byte block[36][18]; + Byte blinded[36][18]; + Byte bflash[36][18]; + Byte flash[20]; + Byte dirted[36][18]; + Byte tmp[33][18]; + //unit block moved in last Player_check_link anim + bool moved[36][18]; + struct { + Byte x; //position du 'trou' + Byte color; + Byte blind_time; + Word hole_pos; //Hole positions + bool final; + } bon[20]; //les lignes chiantes en attente + Byte last_x; // position du dernier bloc depose (pour envoie des lignes) + char snapshot[401]; + Word best_move; //MSB: depth, LSB: complexity + Word best_clean; //same here + Word best_recurse; //MSB: complexity, LSB: depth + Bloc* bloc, *next, *next2, *next3, *bloc_shadow; + Byte color_flash; + Zone_next* znext, *znext2, *znext3; + char msg1[64], msg2[64]; + Dword frame_start; + Byte attacks[MAXPLAYERS], last_attacker; + Inter *inter; + Overmind *over; + Executor *myself; + int handicap; + int level, speed, player; // player: numero de joueur LOCAL (0 a 2) + int num_player; // num_player: numero de joueur dans net_list (0 a MAXPLAYERS) + int h_repeat, h_repeat_delay; + int v_repeat, v_repeat_delay; + int continuous; + int level_up; + int side_speed, down_speed, depth, complexity, lines, bonus; + bool send_for_clean; + int x,y; + char name[40]; + Byte player_hash[16]; + char *long_name(bool handi=true, bool gone=true); + char team_name[40]; + Byte team_hash[16]; + Bitmap *fond; + Video_bitmap *screen; + Sprite *sprlevel_up; + bool smooth; + bool shadow; + void add_text_scroller(const char *st, int xoffset=4, int yoffset=0); + void blind_all(Byte time); + void calc_speed(); + void set_next(); + void set_message(const char *m1, const char *m2); + void change_level_single(); + static void change_level(const int level, Palette *pal, Bitmap *bit); + void delete_bloc(); + void draw_block(int j, int i) const; + void init_block(); + //After a call to collide (or check_collide) that returned + // true, collide_side_only says if the collision + // was solely with the canvas' side walls. + bool collide_side_only; + bool check_collide(Bloc *blo, Byte px, Byte py, Byte rot); + bool collide(Byte px, Byte py, Byte rot); + void clear_key_all(); + void calc_shadow(); + void init(); + void reinit(); + void restart(); + void clear_tmp(); + void give_line(); + void step_bflash(); + void blit_bloc(Bloc *blo); + void blit_level_up(); + void blit_back(); + void blit_flash(); + void small_blit_bloc(Bloc *blo); + void small_blit_back(); + void small_draw_block(int j, int i) const; + void small_blit_flash(); + Byte check_key(int i); + void clear_key(int i); + void unrelease_key(int i); + void dirt_rect(int x1, int y1, int w1, int h1); + void add_packet(Canvas *sender, Byte nb, Byte nc, Byte lx, Attack attack, Word hole_pos[]); + void setscreen() { + screen->setmem(); + } + //Stuff de recording multi-player + Packet_clientmoves *moves; + void start_moves(); + void send_p_moves(); + void start_byte(); + void set_bit(int v); + void write_byte(); + Buf da_moves; + Canvas(int game_seed, Byte team, const char *nam, int ph_repeat, int pv_repeat, bool psmooth, bool pshadow, int phandicap, Net_connection *adr, int qplayer, bool wait_down); + Canvas(int qplayer, int game_seed, Palette *p); + virtual ~Canvas(); + void set_canvas_pos(int px, int py, Bitmap *fo, Video_bitmap *s, Zone_next *z, Zone_next *z2, Zone_next *z3, Inter *in); + void hide(); + bool islocal() const; +}; + +#endif diff --git a/include/chat_text.h b/include/chat_text.h new file mode 100644 index 0000000..723ec1d --- /dev/null +++ b/include/chat_text.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_CHAT_TEXT +#define _HEADER_CHAT_TEXT + +#include "net_stuff.h" +#include "inter.h" + +#define CHAT_NBLINE 22 + +class Chat_text: public Net_callable { + friend class Chat_interface; + struct { + char text[256]; + int team; + int color_cut; + } list[CHAT_NBLINE]; + Fontdata *font; + void scroll_up(); + bool new_text; + int w; +public: + static int quel_player; + int to_player; + Chat_text(Fontdata *f, int wid); + void add_text(int team, const char *text, bool sound=true); + void removewatch(); + void addwatch(); + void net_call(Packet *p2); + void clear(); +}; + +extern Chat_text *chat_text; + +void message(int color, const char *text, bool sound=true, bool in_packet=true, bool trusted=false, Net_connection *but=NULL); + +#endif diff --git a/include/color.h b/include/color.h new file mode 100644 index 0000000..ffda133 --- /dev/null +++ b/include/color.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_COLOR +#define _HEADER_COLOR + +#include "palette.h" + +class Color { + Byte base; + Palette& pal; +public: + Color(Byte q, Palette& p); + void set(int r, int g, int b, int r2, int g2, int b2); + Byte shade(Byte i) const { + return base+i; + } +}; + +#endif diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..9a8a72e --- /dev/null +++ b/include/config.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_CONFIG +#define _HEADER_CONFIG + +#include "types.h" + +class Config { + int version; +public: + const static int game_version; + static int net_version; + const static int major, minor, patchlevel; + static bool registered; + static bool xtreme; + static char user_name[]; + int warning; + struct { + int language; + int setup_player, cdmusic; + Byte multi_level, unlock_theme; + int port_number, mouse_speed; + Byte pane[3]; + Byte update_rate; + char book[10][256]; + char game_name[32]; + char game_server_address[256]; + int game_type, level_up, level_start, combo_min, game_end, game_end_value, game_public; + } info; + struct { + char name[40]; + int color, shadow, smooth, repeat; + int key[5]; + } player[3]; + //The 'stuff' things are for future expansion + struct { + int handicap; + char ngPasswd[64]; + char ngTeam[40]; + char filler1[24]; + char ngTeamPasswd[64]; + int key[2]; + int h_repeat, v_repeat; + int continuous; + int whole_bunch_of_stuff[11]; + } player2[3]; + struct { + int some_stuff[16]; + char yet_more_stuff[64]; + } info2; + char fname[1024]; + Config(); + virtual ~Config(); + void default_config(); + void read(); + void write(); + void check_register(); +}; + +extern Config config; + +#endif diff --git a/include/fonts.h b/include/fonts.h new file mode 100644 index 0000000..032ca8c --- /dev/null +++ b/include/fonts.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_FONTS +#define _HEADER_FONTS + +class Fontdata; + +class Fonts { +public: + Fontdata *normal, *courrier; + void init(); + void deinit(); +}; + +extern Fonts fonts; + +#endif diff --git a/include/game.h b/include/game.h new file mode 100644 index 0000000..0d91b75 --- /dev/null +++ b/include/game.h @@ -0,0 +1,170 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_GAME +#define _HEADER_GAME + +enum Attack_type { + ATTACK_LINES, + ATTACK_NONE, + ATTACK_BLIND, + ATTACK_FULLBLIND, + ATTACK_LAST +}; + +class Attack { +public: + Attack_type type; + int param; + Attack() { + type=ATTACK_LINES; + param=0; + } + char *log_type() { + switch(type) { + case ATTACK_LINES: return "lines"; + case ATTACK_NONE: return "none"; + case ATTACK_BLIND: return "blind"; + case ATTACK_FULLBLIND: return "fullblind"; + default: return "unknown"; + } + return "unknown"; + } +}; + +enum End_type { + END_NEVER, + END_FRAG, + END_TIME, + END_POINTS, + END_LINES, + END_LAST +}; + +#include "track.h" +#include "array.h" +#include "types.h" +#include "buf.h" +#include "net_list.h" +#include "stats.h" + +class Packet; +class Packet_gameserver; +class Net_connection; +class Net_server; +class Net_client; +class Qserv; +class Game_params; +class Recording; + +//Gotchas: +//Constructing a Game sets ::game to this +//Destroying a Game sets ::game to NULL +class Game: public GS { + TRACKED; + friend class Net_list; + Array stack; + Qserv *gameinfo; + bool http_failed; + void buildgameinfo(); + char record_filename[1024]; + char slog_filename[1024]; + bool is_recording, is_slogging; + Dword last_given_potato; + Byte the_net_version; +public: + int net_version(); //Required net_version to play this game + bool auto_restart; + void stop_stuff(); + void restart(); + void count_playing_time(); + void addgameinfo(Textbuf *tb); + Dword frame_start; + bool wants_moves; + Net_server *net_server; + Net_client *net_client; + bool single; + bool network; + bool survivor; + bool hot_potato; + // Potato stuff (server only) + int potato_lines[MAXTEAMS]; + Byte potato_order[MAXTEAMS]; + Byte potato_team; + Byte previous_potato_team; + void reset_potato(); + void new_potato_order(); + void got_potato(Byte team, int lines); + void done_potato(Byte team); + void check_potato(); + Byte next_potato_team(); + int server_accept_player, server_accept_connection; + Net_connection *loopback_connection; + Net_list net_list; + bool valid_frag; //Survivor only + bool server, abort, level_up, terminated, game_public; + Byte level_start, combo_min; + End_type game_end; + bool allow_handicap; + char name[32]; + int seed, game_end_value; + Attack normal_attack, potato_normal_attack; + Attack clean_attack, potato_clean_attack; + bool any_attack(); + bool paused; + Word delay_start; + Game(Packet_gameserver *p); + Game(Game_params* p); + virtual ~Game(); + void clientpause(); + void stackpacket(Packet *p); + Packet *peekpacket(Byte type); + void removepacket(); + void sendgameinfo(bool quit); + void stepgameinfo(); + bool gameinfo_completed() const; + void endgame(); + void prepare_recording(const char *fn); + void prepare_logging(const char *filename); + int get_multi_level(); + void set_seed(Packet_serverrandom *p); + char *get_motd(); +}; + +enum Game_preset { + PRESET_FFA, + PRESET_SURVIVOR, + PRESET_PEACE, + PRESET_BLIND, + PRESET_FULLBLIND, + PRESET_HOT_POTATO, + PRESET_SINGLE, + PRESET_SINGLE_SPRINT, + PRESET_LAST +}; + +class Game_params { +public: + const char *name; + bool single; + bool survivor; + bool hot_potato; + Attack normal_attack, potato_normal_attack; + Attack clean_attack, potato_clean_attack; + bool level_up; + int level_start; + bool allow_handicap; + End_type game_end; + int game_end_value; + bool game_public; + bool network; + void set_preset(Game_preset preset); + Game_params(); +}; + +extern Game *game; + +#endif diff --git a/include/game_menu.h b/include/game_menu.h new file mode 100644 index 0000000..9231143 --- /dev/null +++ b/include/game_menu.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_GAME_MENU +#define _HEADER_GAME_MENU + +#include "net_stuff.h" +#include "net_call.h" + +class Game; +class Stringtable; + +class Create_game: public Menu, public Notifyable { + static int game_end_y; + Bitmap *bit_; + Zone *start, *cancel, *save; + Zone *z_record_name, *z_slog_name; + char name[32], record_name[32], slog_name[32]; + Observable *game_type, *game_end_watch; + Observable *record_watch, *slog_watch; + Zone *record_zone, *slog_zone; + int selected, level_up, game_end, game_end_value, game_public; + int record_game, slog; + Zone_text *game_end_text; + Zone_text *game_desc[10]; + Stringtable *game_descriptions; + Zone *game_end_selector; + Zone_input_numeric *game_end_num; + void save_setting(); + bool net_game, local_net; + void recreate_game_end(); +public: + Create_game(Bitmap *bit, Font *font, Font *font2, const Palette& p, bool pnet_game, bool plocal_net); + virtual ~Create_game(); + virtual void step(); + virtual void notify(); +}; + +class Create_game_start: public Module { + Bitmap *bit_; + Font *font_; + const Palette &pal; +public: + Create_game_start(const Palette &pal_, Bitmap *bit, Font *font); + virtual ~Create_game_start(); + virtual void init(); +}; + +class Create_game_end: public Menu { + Zone *cancel; + const Palette &pal; +public: + Create_game_end(const Palette &pal_, Bitmap *bit, Font *font); + virtual ~Create_game_end(); + virtual void step(); + virtual void init(); +}; + +class Join_game: public Menu, public Net_callable { + Zone *cancel; + Zone_text *status; + Packet_wantjoin pac; + Exec_ping *eping; + int delay; + Bitmap *bit_; + Font *font2_; + Dword address; + int port; + bool rejoin; +public: + Join_game(Bitmap *bit, Font *font, Font *font2, const Palette& p, const char *n, Dword sa, int sport, bool prejoin); + virtual ~Join_game(); + virtual void init(); + virtual void step(); + virtual void net_call(Packet *p2); +}; + +class Join_download: public Menu { + Bitmap *bit_; + Font *font2_; + int nb_total, nb_current, nb_percent; + bool rejoin; +public: + Join_download(Bitmap *bit, Font *font, Font *font2, const Palette& p, bool prejoin); + virtual ~Join_download(); + virtual void step(); +}; + +#endif diff --git a/include/global.h b/include/global.h new file mode 100644 index 0000000..9e99252 --- /dev/null +++ b/include/global.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_GLOBAL +#define _HEADER_GLOBAL + +#define MAXPLAYERS 8 +#define MAXTEAMS 8 + +extern char st[]; +extern char quadradir[]; +extern char team_name[MAXTEAMS][40]; +extern bool named_team[MAXTEAMS]; +extern const char *english_teams[]; +extern const char *french_teams[]; + +void set_team_name(Byte team, const char *name); + +extern bool quitting; +extern const char built[]; + +void quit_fast(); + +#endif diff --git a/include/highscores.h b/include/highscores.h new file mode 100644 index 0000000..1b5b6cc --- /dev/null +++ b/include/highscores.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_HIGHSCORES +#define _HEADER_HIGHSCORES + +#include "stats.h" + +class Canvas; +class Playback; + +#define MAX_SCORE 5 + +class Highscores { + static bool loaded; + Highscores(); //statics only, don't instantiate +public: + struct Best { + char name[40]; + int score, lines, level; + Playback* demo; + }; + //You must call 'load' before using these. I know it sucks + static int numLocal; + static Best bestlocal[MAX_SCORE]; + static int numGlobal; + static Best bestglobal[MAX_SCORE]; + static void getFilename(char* st, int i); + static void getGlobalFilename(char* st, int i); + static void load(); + static void free(); + static int update(Canvas* c); +}; + +#endif diff --git a/include/menu.h b/include/menu.h new file mode 100644 index 0000000..fc18dde --- /dev/null +++ b/include/menu.h @@ -0,0 +1,358 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MENU +#define _HEADER_MENU + +#include "sound.h" +#include "sprite.h" +#include "inter.h" +#include "listbox.h" +#include "zone_list.h" +#include "stats.h" +#include "net_stuff.h" +#include "menu_base.h" +#include "score.h" +#include "highscores.h" +#include "game.h" + +class Pane_option; +class Pane_info; + +class Menu_do_nothing: public Menu { +public: + //It actually does something: + // wait for quitting==true and then ret() + virtual void step(); +}; + +class Menu_intro: public Menu { + int warning; + Font *font2; + bool once; +public: + Menu_intro(); + virtual ~Menu_intro(); + virtual void init(); + virtual void step(); +}; + +class Menu_guy: public Menu { + Sample *son; +public: + Menu_guy(); + virtual ~Menu_guy(); + virtual void init(); + virtual void step(); +}; + +class Menu_ugs: public Menu { + Sample *son; +public: + Menu_ugs(); + virtual ~Menu_ugs(); + virtual void init(); + virtual void step(); +}; + +class Menu_main_startmusic: public Module { +public: + virtual void init(); +}; + +class Menu_main: public Menu { + Zone_bitmap *z_back; + Zone *b_single, *b_multi, *b_setup, *b_help, *b_quit; + Zone *b_tut, *b_option, *b_logo, *b_demo; + int delay; + bool old_registered; + int old_language; + void reset_delay(); + void redraw(); +public: + Menu_main(); + virtual void init(); + virtual void step(); +}; + +class Menu_quitgame: public Menu_standard { + Font *font2; +public: + Menu_quitgame(); + virtual ~Menu_quitgame(); + virtual void init(); + virtual void step(); +}; + +class Menu_help: public Menu_standard { + Font *font2; + Zone *b_quit; + Zone *b_www, *b_email, *b_online, *b_register; + void call_internet(const char *s); +public: + Menu_help(); + virtual void init(); + virtual void step(); +}; + +class Menu_register: public Menu_quit { + Zone *b_quit, *b_ok, *z_invalid; + Zone_text_input *z_pass; + char name[64]; + char pass[64]; +public: + Menu_register(Inter *in, const Palette &p); + virtual void step(); +}; + +class Menu_option: public Menu_standard { + Bitmap *bit; + Font *font2; + Zone *b_quit; + int old_language,old_music,old_mouse_speed,old_port; + char old_server[256]; +public: + Menu_option(); + virtual ~Menu_option(); + virtual void step(); +}; + +class Zone_color_select; +class Zone_set_key; + +class Menu_setup: public Menu_standard { + Zone *b_quit, *b_player, *b_all_key; + Zone_text_input *z_nameinput; + Zone_text_input *z_passwdinput; + Zone_text_input *z_tnameinput; + Zone_text_input *z_tpasswdinput; + Zone_state_text *z_continuousdown; + Zone_state_text *z_h_repeat; + Zone_state_text *z_v_repeat; + Zone_state_text *z_shadow; + Zone_state_text *z_smooth; + Zone_set_key* z_key[7]; + Zone* b_key[7]; + Bitmap *bit; +public: + Menu_setup(); + virtual ~Menu_setup(); + virtual void step(); +}; + +class Menu_setup_all_key: public Menu { + Zone_set_key **key; + Bitmap *bit; + Zone_text *z_text; + int quel; +public: + Menu_setup_all_key(Inter *in, Zone_set_key *k[]); + virtual void step(); +}; + +class Menu_setup_key: public Menu { + Zone_set_key *key; +public: + Menu_setup_key(Inter *in, Zone_set_key *k, const char *t); + Menu_setup_key(Inter *in, Zone_set_key *k); + virtual void init(); + virtual void step(); +}; + +class Menu_single: public Menu_standard { + Zone* normal, *sprint, *cancel; + Bitmap *bit; + Font *font2; +public: + Menu_single(); + virtual ~Menu_single(); + virtual void step(); +}; + +class Menu_multi: public Menu_standard { + Zone* local, *net_lan, *net_internet, *cancel; + Bitmap *bit; + Font *font2; +public: + Menu_multi(); + virtual ~Menu_multi(); + virtual void step(); +}; + +class Menu_multi_join: public Menu, Net_callable { + friend class Menu_multi_refresh; + friend class Menu_multi_internet; + Zone *b_create, *b_refresh, *b_join, *cancel, *b_info, *b_book; + Zone *b_refresh_internet; + Zone_text_field *z_game_status, *z_game_end, *z_game_minimum, *z_game_level, *z_game_speed, *z_game_type; + Zone_listbox *list_game; + Zone_listbox *list_player; + int selected_game; + Bitmap *bit_; + Font *font2_; + bool local_net; + char address[256]; + char game_end_text[256]; + int game_level_start; + void refresh_player(); + void clear_game_info(); +public: + Menu_multi_join(Bitmap *bit, Font *font, Font *font2, const Palette& p, bool plocal); + virtual ~Menu_multi_join(); + virtual void init(); + virtual void step(); + void addwatch(); + void removewatch(); + void refresh(); + virtual void net_call(Packet *p2); + void join_game(char *nam, Dword adr, int port); +}; + +class Zone_input_address: public Zone_text_input { + Menu_multi_join *parent; +public: + Zone_input_address(Menu_multi_join *p, const Palette &pal, Inter* in, char* s, int mlen, int px, int py, int pw): + Zone_text_input(in, pal, s, mlen, px, py, pw) { + parent = p; + } + virtual void lost_focus(int cancel); +}; + +class Menu_multi_refresh: public Menu { + Menu_multi_join *parent; + Zone *cancel; + void find_local_games(); + void find_internet_games(); + void resolve(); +public: + Menu_multi_refresh(Menu_multi_join *p); + virtual void init(); + virtual void step(); +}; + +class Qserv; + +class Menu_multi_internet: public Menu { + Menu_multi_join *parent; + Zone *cancel; + Qserv *request; + Attack parse_attack(const char *s); + void parsegames(); +public: + Menu_multi_internet(Menu_multi_join *p); + virtual ~Menu_multi_internet(); + virtual void init(); + virtual void step(); +}; + +class Menu_highscore: public Menu_standard, public Zone_list { + Bitmap *bit; + Font *font2, *courrier, *courrier2; + Zone *b_quit, *b_again, *playdemo[MAX_SCORE], *playdemog[MAX_SCORE], *playlast; + Zone_text_button *sync; + Qserv *sync_request; + char *file_name; + int *play_again; + int time_demo; + bool show_playback; + Zone_text_field *status; + void play_demo(const char *st); + void start_sync(); + void step_sync(); + void stop_sync(); + void refresh_global(int& y); +public: + Menu_highscore(int hscore=-1, int *playagain=NULL, bool show_playb=false); + virtual ~Menu_highscore(); + virtual void step(); +}; + +class Menu_stat: public Menu_standard, public Notifyable, Zone_list { + class Colonne { + public: + int width, page; + CS::Stat_type quel_stat; + char titre[32]; + Zone_text_button *z_titre; + bool sort_me; + Colonne(); + void set_titre(const char *s); + }; + Array col; + Bitmap *bit; + Font *font2; + Zone *b_quit, *b_restart, *b_stop; + Array b_page; + Score score; + Font *fcourrier[MAXTEAMS]; + int active_sort, active_page; + const int c_start; + const int c_gap; + void display(); + void calculate_total(bool force_blit); + void add_title(Colonne &col, int *px, Bitmap *bit); + void set_sort(int quel); + void change_page(int p); + void init_columns(Bitmap *bit); + void sort(int quel_stat, int new_order[], bool reversed=false); +public: + Menu_stat(); + virtual ~Menu_stat(); + virtual void init(); + virtual void step(); + virtual void notify(); +}; + +class Menu_multi_checkip: public Menu { + Bitmap *bit_; + Zone *cancel; +public: + Menu_multi_checkip(Bitmap *bit, Font *font, Font *font2, const Palette& p); + virtual void step(); +}; + +class Menu_multi_book: public Menu { + Bitmap *bit_; + Font *font2_; + Zone *cancel; + char buf[10][256]; + Zone *b_connect[10]; + Zone_text_field *status; + bool looking, connect_failed; + const char* address; +public: + Menu_multi_book(Bitmap *bit, Font *font, Font *font2, const Palette& p, const char *adr); + virtual void init(); + virtual void step(); +}; + +class Menu_internet: public Menu { + const char *command; +public: + Menu_internet(const char *c); + virtual void init(); + virtual void step(); +}; + +class Menu_startserver: public Menu_quit { + Bitmap *bit; +public: + Menu_startserver(); + virtual ~Menu_startserver(); + virtual void step(); +}; + +class Menu_startconnect: public Menu_quit { + Bitmap *bit; + Module *module; + Font *font2; +public: + Menu_startconnect(const char *adr, bool rejoin); + virtual ~Menu_startconnect(); + virtual void step(); +}; + +#endif diff --git a/include/menu_base.h b/include/menu_base.h new file mode 100644 index 0000000..aeca099 --- /dev/null +++ b/include/menu_base.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MENU_BASE +#define _HEADER_MENU_BASE + +#include "overmind.h" +#include "misc.h" +class Inter; +class Zone; +class Bitmap; +class Font; + +class Menu_quit: public Menu { +protected: + bool quit; +public: + Menu_quit(Inter *base=NULL): Menu(base) { + quit = false; + } + virtual void step(); +}; + +class Menu_standard: public Menu_quit { +public: + Menu_standard(Inter *base=NULL): Menu_quit(base) { } + virtual void init() { + Menu_quit::init(); + call(new Fade_in(pal)); + } +}; + +class Menu_fadein: public Menu { +public: + Menu_fadein(Inter *base=NULL): Menu(base) { } + virtual void init() { + Menu::init(); + call(new Fade_in(pal)); + } +}; + +class Menu_net_problem: public Menu { + Zone *cancel; +public: + Menu_net_problem(const char *s, const char *context, Bitmap *bit, Font *font); + virtual void step(); +}; + +class Call_setfont: public Module { + Module *module; + const Palette &pal; +public: + Call_setfont(const Palette &p, Module *m); + virtual void init(); + virtual void step(); +}; + +#endif diff --git a/include/menu_demo_central.h b/include/menu_demo_central.h new file mode 100644 index 0000000..c6d46e0 --- /dev/null +++ b/include/menu_demo_central.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MENU_DEMO_CENTRAL +#define _HEADER_MENU_DEMO_CENTRAL + +#include "menu_base.h" +#include "zone_list.h" +#include "score.h" + +class Zone; +class Bitmap; +class Zone_listbox; +class Zone_text_input; +class Zone_text; +class Res; +class Playback; +class Dict; + +class Menu_demo_central: public Menu_standard, Zone_list { + class Listitem: public Listable { + public: + bool isfolder; + char file_date[1024]; + Dword file_size; + Listitem(const char *n, Font *f); + virtual ~Listitem(); + }; + class Player_infos { + public: + Byte team; + Byte player; + char name[40]; + Player_infos(int pplayer); + }; + Score score; + Array pinfos; + Zone_text_field *z_status, *z_name, *z_date, *z_version, *z_duration, *z_type, *z_end; + Zone *z_play, *z_delete; + char s_date[1024], s_version[1024], s_duration[1024], s_type[1024], s_end[1024], s_name[1024]; + Playback *play; + Font *fcourrier[MAXTEAMS]; + void clear_detail(); + void refresh_detail(); + void drive_playback(const char *n); + void populate_dict(Dict *d); +public: + Zone *cancel; + Bitmap *bit; + Zone_listbox *z_list; + Zone_text_input *z_dir; + int quel; + char find_directory[1024]; + Menu_demo_central(); + virtual ~Menu_demo_central(); + virtual void step(); + void reload(); +}; + +#endif diff --git a/include/misc.h b/include/misc.h new file mode 100644 index 0000000..18d8b11 --- /dev/null +++ b/include/misc.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MISC +#define _HEADER_MISC + +#include "overmind.h" +#include "palette.h" + +class Fade_to: public Module { + Fade* fad; +public: + Fade_to(const Palette& dst, const Palette& src, int nframe=16); + virtual ~Fade_to(); + virtual void step(); +}; + +class Fade_in: public Fade_to { +public: + Fade_in(const Palette& dst): Fade_to(dst, noir) { } + virtual void init(); +}; + +class Fade_out: public Fade_to { +public: + Fade_out(const Palette& src): Fade_to(noir, src) { } + virtual void init(); +}; + +class Setpalette: public Module { + const Palette& pal; +public: + Setpalette(const Palette& p); + virtual void init(); +}; + +class Wait_event: public Module { +public: + virtual void step(); +}; + +class Wait_time: public Wait_event { + int delay; + bool force_wait; +public: + Wait_time(int del, bool force=false); + virtual void step(); +}; + +#endif diff --git a/include/multi_player.h b/include/multi_player.h new file mode 100644 index 0000000..bcbf83e --- /dev/null +++ b/include/multi_player.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MULTI_PLAYER +#define _HEADER_MULTI_PLAYER +#include "overmind.h" +#include "inter.h" +#include "bitmap.h" +#include "misc.h" +#include "menu_base.h" +#include "zone_list.h" +#include "zone.h" +#include "game.h" + +class Pane; +class Pane_info; +class Playback; + +class Zone_slow_play: public Zone_text_button2 { +public: + Zone_slow_play(Inter *in, Bitmap *bit, Font *f, const char *t, int px, int py); + virtual void process(); + virtual void waiting(); + virtual void clicked(int quel) { } +}; + +class Zone_fast_play: public Zone_text_button2 { +public: + Zone_fast_play(Inter *in, Bitmap *bit, Font *f, const char *t, int px, int py); + virtual void process(); + virtual void waiting(); + virtual void clicked(int quel) { } +}; + +class Multi_player: public Menu_fadein, Zone_list { + int *got_highscore; + Zone *b_quit; + int last_countdown; + Module **menu_stat; +public: + bool stop; + Bitmap *bit; + Font *font2, *courrier; + Executor *pane_exec[3]; + Pane *pane[3]; + Pane_info *pane_info[3]; + bool pause; + Zone_sprite *zone_pause; + int time_demo; + void check_pause(); + Multi_player(int *got_high); + virtual ~Multi_player(); + virtual void step(); + void set_menu_stat(Module **module); +}; + +class Demo_multi_player: public Module { + void init_playback(); +public: + Demo_multi_player(Playback *p); + Demo_multi_player(Res *r, bool auto_demo=false); + virtual ~Demo_multi_player(); + virtual void init(); +}; + +class Single_player: public Module { + int play_again; + const Game_preset gp; +public: + Single_player(Game_preset pgp); + virtual void step(); +}; + +class Single_player_iterate: public Module { + int *play_again, hscore; + const Game_preset gp; +public: + Single_player_iterate(int *play, Game_preset pgp); + virtual void init(); + virtual void step(); +}; + +class Multi_player_launcher: public Module { + Module *menu; +public: + virtual void init(); + virtual void step(); +}; + +#endif diff --git a/include/multi_provider.h b/include/multi_provider.h new file mode 100644 index 0000000..b25fae6 --- /dev/null +++ b/include/multi_provider.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MULTI_PROVIDER +#define _HEADER_MULTI_PROVIDER +#include "overmind.h" +#include "inter.h" +#include "bitmap.h" + +class Menu_multi_provider: public Menu { + Bitmap *bit; + Font *font, *font2; + const Palette &pal2; + int quel; +public: + Menu_multi_provider(int q, Bitmap *b, Font *f, Font *f2, const Palette &p); + virtual void step(); +}; + +#endif diff --git a/include/net_list.h b/include/net_list.h new file mode 100644 index 0000000..971135f --- /dev/null +++ b/include/net_list.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NET_LIST +#define _HEADER_NET_LIST + +#include "array.h" +#include "notify.h" +#include "net.h" +#include "score.h" +#include "overmind.h" +#include "net_stuff.h" +#include "stats.h" +#include "game.h" + +class Canvas; + +class Lastline { +public: + Net_connection *nc; + char cmd[256], params[256]; + void set(const char *pcmd, const char *pparams) { + strncpy(cmd, pcmd, 255); + cmd[255] = 0; + strncpy(params, pparams, 255); + params[255] = 0; + } + Lastline(Net_connection *pnc, const char *pcmd, const char *pparams) { + nc = pnc; + set(pcmd, pparams); + } +}; + +class Net_list_stepper; +class Packet_dropplayer; +class Packet_lines; +class Packet_serverrandom; + +class Net_list: public Observable { + friend class Net_list_stepper; + friend class Game; + void step_all(); + Net_list_stepper *stepper; //Overmind thread that will call step_all + Canvas *list[MAXPLAYERS]; + Dword last_use[MAXPLAYERS]; + bool end_signaled, demo_completed; + bool winner_signaled; + void check_drop(); + void check_gone(); + void check_potato(); + bool check_first_frag(); //Returns true if it's a good time to end the game + void check_pause(); + void check_end_game(bool end_it); + void check_player(); + void check_admin(); + void check_stat(); + Array cmd_cache; + Array deny_list; + Array allow_list; + Dword lastgameinfo; + int *objectives; + bool reached[10][MAXTEAMS]; //Max 10 objectives + //Return which goal was last attained (if any) since last call, + // given the team number and goals remaining + int check_goals(Byte team, int remain); + Dword gone_time_limit; + Dword ppm_limit; + Dword lag_limit; + char admin_password[64]; + char motd[256]; + Byte idle_on_last_notify[MAXPLAYERS]; +public: + virtual void notify_all(); + CS::Stat_type goal_stat; + Score score; + void reset_objectives(); //Game calls this upon construction + void team2name(Byte team, char *st); + void update_team_names(); + void send_end_signal(bool auto_end); + void restart(); + void got_admin_line(const char *line, Net_connection *nc); + bool accept_connection(Net_connection *nc); + void client_deconnect(Net_connection *nc); + Byte syncpoint; + int size(); + int add_player(Canvas *c); + void set_player(Canvas *c, int pos, bool msg); + //Should be called on server only: initiate drop player procedure + void server_drop_player(Byte player, Drop_reason reason); + //Should be called on client or server when receiving + // Packet_dropplayer, to finish drop player procedure + void drop_player(Packet_dropplayer *p, bool chat); + void remove_player(Canvas *c); + void rejoin_player(Canvas *c); + int canvas2player(Canvas *c); + void sendlines(Packet_lines *p); + void send(Canvas *c, Byte nb, Byte nc, Byte lx, Attack attack, bool clean); + void pause_all(); + void unpause_all(); + Canvas *get(int i) const { + return list[i]; + } + Dword gettimer() const; + bool competitive() const; + bool would_be_competitive() const; + bool all_dead_or_gone() const; + bool all_gone() const; + void syncto(Byte syncpoint); + Net_list(); + virtual ~Net_list(); +}; + +class Net_list_stepper: public Module_thread { + Net_list *the_net_list; +public: + Net_list_stepper(Net_list *nl); + virtual void step(); +}; + +#endif diff --git a/include/net_server.h b/include/net_server.h new file mode 100644 index 0000000..de0ae08 --- /dev/null +++ b/include/net_server.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NET_SERVER +#define _HEADER_NET_SERVER + +#include "types.h" +#include "net_stuff.h" +#include "overmind.h" + +class Net_client: public Net_callable { + void pause(Packet *p2); +public: + Net_client(); + virtual ~Net_client(); + virtual void net_call(Packet *p2); +}; + +class Recording; + +class Net_server: public Net_callable { + Array pendings; + void playerwantjoin(Packet *p2); + void findgame(Packet *p2); + void wantjoin(Packet *p2); + void clientstartwatch(Packet *p2); + void clientmoves(Packet *p2); + void clientchat(Packet *p2); +public: + void clientpause(Packet *p2); + bool allow_start; + bool allow_pause; + void record_packet(Packet *p2); + void stop_multi_recording(); + Net_server(); + virtual ~Net_server(); + virtual void net_call(Packet *p2); +}; + +class Net_pendingjoin: public Module, public Notifyable { + Packet_wantjoin *pac; + bool cancel; +public: + Net_pendingjoin(Packet_wantjoin *p); + virtual ~Net_pendingjoin(); + virtual void step(); + virtual void notify(); + static void load_packet_gameserver(Packet_gameserver* resp); +}; + +#endif diff --git a/include/net_stuff.h b/include/net_stuff.h new file mode 100644 index 0000000..9c61a3f --- /dev/null +++ b/include/net_stuff.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NET_STUFF +#define _HEADER_NET_STUFF + +enum Drop_reason { + DROP_AUTO, + DROP_MANUAL, + DROP_INVALID_BLOCK, + DROP_LAST +}; + +#include "types.h" +#include "array.h" +#include "global.h" +#include "packets.h" +#include "net.h" +#include "net_call.h" +#include "overmind.h" + +class Net_starter { + class Net_module: public Module { + Dword last_video_frame; + public: + Net_module(); + virtual void step(); + }; + Executor *net_exec; +public: + Net_starter(); + virtual ~Net_starter(); +}; + +extern Net_starter* net_starter; + +class Quadra_param: public Net_param { +public: + virtual int tcpport(); + virtual void print_packet(Packet *p2, char *st); + virtual Packet *alloc_packet(Word pt); + virtual bool is_dispatchable(Net_connection *nc, Packet *p); + virtual Dword magic() { + return ('R'<<24) | ('M'<<16) | ('T'<<8) | ('3'); + } + virtual void server_deconnect(); + virtual void client_connect(Net_connection *adr); + virtual void client_deconnect(Net_connection *adr); + virtual bool accept_connection(Net_connection *nc); + virtual char *get_motd(); +}; + +extern void send_msg(Net_connection *nc, char *msg, ...); + +#endif diff --git a/include/nglog.h b/include/nglog.h new file mode 100644 index 0000000..ec8787c --- /dev/null +++ b/include/nglog.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NGLOG +#define _HEADER_NGLOG + +#include "res.h" + +class Log { + Res_dos *file; + char filename[1024]; +public: + bool exist; + Log(const char *fname); + virtual ~Log(); + void log_event(const char *st); +}; + +bool log_init(const char *filename); +void log_step(const char *st, ...); +void log_finalize(char *salt); + +char *log_team(int t); +char *log_handicap(int h); + +#endif diff --git a/include/packets.h b/include/packets.h new file mode 100644 index 0000000..cb93963 --- /dev/null +++ b/include/packets.h @@ -0,0 +1,643 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PACKETS +#define _HEADER_PACKETS + +#include "packet.h" +#include "array.h" +#include "net_stuff.h" +#include "game.h" +#include "config.h" +#include "track.h" + +enum Packet_type { + P_CHAT, + P_FINDGAME, + P_GAMESERVER, + P_WANTJOIN, + P_PLAYERWANTJOIN, + P_CLIENTCHAT, + P_PLAYERACCEPTED, + P_PLAYER, + P_CLIENTPAUSE, + P_PAUSE, + P_STAT, + P_GAMEINFO, + P_DROPPLAYER, + P_CLIENTDROPPLAYER, + P_SERVERDROPPLAYER, + P_STAMPBLOCK, + P_CLIENTSTAMPBLOCK, + P_DEAD, + P_CLIENTDEAD, + P_RESPAWN, + P_CLIENTRESPAWN, + DEPRECATED_P_WATCH, + P_STARTWATCH, + P_CLIENTSTARTWATCH, + P_DOWNLOAD, + P_CLIENTLINES, + P_LINES, + P_CLIENTTESTPING, + P_TESTPING, + P_FIRST_FRAG, + P_CLIENTFIRST_FRAG, + P_GONE, + P_CLIENTGONE, + P_ENDGAME, + P_REJOIN, + P_MOVES, + P_CLIENTMOVES, + P_STATE, + P_CLIENTSTATE, + P_SERVERSTATE, + P_SERVERRANDOM, + P_SERVERPOTATO, + DEPRECATED_P_SERVERLINES, + P_SERVERTESTPING, + P_BYE, + P_REMOVEBONUS, + P_CLIENTREMOVEBONUS, + P_SERVERNAMETEAM, + P_GAMESTAT +}; + +class Packet_findgame: public Packet_udp { +public: + Packet_findgame() { + packet_id = P_FINDGAME; + } +}; + +class Packet_wantjoin: public Packet_ping { +public: + Byte net_version; + Byte language; + Byte os; + bool registered; + Packet_wantjoin() { + packet_id = P_WANTJOIN; + net_version=Config::net_version; + language=config.info.language; + os= + #if defined(UGS_DIRECTX) + 1 + #elif defined(UGS_LINUX) + 2 + #else + #error "What platform???" + #endif + ; + registered=Config::registered; + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Net_player { +public: + Byte quel; //Not sent over the net in Packet_gameinfo, + // sent only with Packet_gameserver + Byte team; + char name[40]; + Dword player_id; + Byte idle; //This is not sent thru the net, it's only there if + // someone set it from a Qserv query + int handicap; //May not be there, but default of zero is ok in + // those cases + Net_player(Byte q, Byte t, const char *s, Dword pid, int status, int phandicap) { + quel=q; + team=t; + strcpy(name, s); + player_id=pid; + if(status!=-1) + idle=status; + else + idle=255; + handicap=phandicap; + } +}; + +class Packet_gameinfo: public Packet_udp { +public: + Array players; + char name[32]; + Byte version; + int port, game_end_value; + bool nolevel_up, delay_start, terminated; + bool survivor, hot_potato; + Attack normal_attack, clean_attack, potato_normal_attack, potato_clean_attack; + Byte level_start, combo_min, game_end; + bool allow_handicap; + Packet_gameinfo(); + virtual ~Packet_gameinfo(); + void add_player(Byte q, Byte t, const char *s, int status, int handicap) { + players.add(new Net_player(q, t, s, 0, status, handicap)); + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Packet_gameserver: public Packet_ping { +public: + Byte version; + Array players; + char name[32]; + bool accepted; + int game_seed, game_end_value; + bool paused, nolevel_up; + Byte level_start, combo_min; + bool allow_handicap; + bool survivor, hot_potato; + Attack normal_attack, clean_attack, potato_normal_attack, potato_clean_attack; + Byte game_end; + Word delay_start; + bool wants_moves; + Byte syncpoint; + Byte potato_team; + bool single; + bool terminated; + Packet_gameserver() { + packet_id = P_GAMESERVER; + name[0] = 0; + } + virtual ~Packet_gameserver(); + void add_player(Byte q, Byte t, const char *s, Dword pid, int handicap) { + players.add(new Net_player(q, t, s, pid, -1, handicap)); + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); + bool any_attack(); +}; + +class Packet_chat: public Packet_tcp { +public: + signed char team; + signed char to_team; + char text[256]; + Packet_chat() { + packet_id = P_CHAT; + text[0] = 0; + team = -1; + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Packet_clientchat: public Packet_chat { +public: + Packet_clientchat() { + packet_id = P_CLIENTCHAT; + } +}; + +class Packet_playerwantjoin: public Packet_ping { +public: + Byte team; + char name[40]; + Byte player; + int h_repeat, v_repeat, smooth, shadow, handicap; + Byte player_hash[16]; + char team_name[40]; + Byte team_hash[16]; + Packet_playerwantjoin() { + packet_id=P_PLAYERWANTJOIN; + name[0]=0; + team_name[0]=0; + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Packet_player: public Packet_ping { +public: + Byte team; + char name[40]; + Dword player_id; + Byte player; + int h_repeat, v_repeat, smooth, shadow; + Byte pos; + int handicap; + Packet_player() { + packet_id = P_PLAYER; + name[0]=0; + player_id=0; + pos=0; + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Packet_playeraccepted: public Packet_ping { +public: + Byte pos; + Byte accepted; + Packet_playeraccepted() { + packet_id = P_PLAYERACCEPTED; + pos=0; + accepted = 0; + } + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Packet_clientpause: public Packet_tcp { +public: + Packet_clientpause() { + packet_id = P_CLIENTPAUSE; + } +}; + +class Packet_pause: public Packet_tcp { +public: + signed char player; + Packet_pause() { + packet_id = P_PAUSE; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Net_stat { +public: + Byte st; + int value; + Net_stat(Byte s, int v) { + st=s; + value=v; + } +}; + +class Packet_playerbase: public Packet_tcp { + TRACKED; +public: + Byte player; + Packet_playerbase() { + player = 255; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_stat: public Packet_playerbase { +public: + Array net_stats; + Byte num_stat; + Packet_stat() { + packet_id = P_STAT; + } + virtual ~Packet_stat(); + void add_stat(Byte s, int v); + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_gamestat: public Packet_tcp { +public: + Array net_stats; + Byte num_stat; + Packet_gamestat() { + packet_id = P_GAMESTAT; + } + virtual ~Packet_gamestat(); + void add_stat(Byte s, int v); + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_dropplayer: public Packet_playerbase { +public: + Drop_reason reason; + Packet_dropplayer() { + packet_id = P_DROPPLAYER; + reason=DROP_LAST; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientdropplayer: public Packet_dropplayer { +public: + Packet_clientdropplayer() { + packet_id = P_CLIENTDROPPLAYER; + } +}; + +class Packet_serverdropplayer: public Packet_dropplayer { +public: + Packet_serverdropplayer() { + packet_id = P_SERVERDROPPLAYER; + } +}; + +class Packet_stampblock: public Packet_playerbase { +public: + Byte x, y; + Byte rotate; + Byte score; + Word date; + Byte block_rotated; + Word time_held; + Packet_stampblock() { + packet_id = P_STAMPBLOCK; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientstampblock: public Packet_stampblock { +public: + Packet_clientstampblock() { + packet_id = P_CLIENTSTAMPBLOCK; + } +}; + +class Packet_dead: public Packet_playerbase { +public: + bool then_gone; + Packet_dead() { + packet_id = P_DEAD; + then_gone=false; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientdead: public Packet_dead { +public: + Packet_clientdead() { + packet_id = P_CLIENTDEAD; + } +}; + +class Packet_respawn: public Packet_playerbase { +public: + Packet_respawn() { + packet_id = P_RESPAWN; + } +}; + +class Packet_clientrespawn: public Packet_respawn { +public: + Packet_clientrespawn() { + packet_id = P_CLIENTRESPAWN; + } +}; + +class Packet_startwatch: public Packet_playerbase { +public: + Byte update; + Dword address; + bool stop; + Packet_startwatch() { + packet_id = P_STARTWATCH; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientstartwatch: public Packet_startwatch { +public: + Packet_clientstartwatch() { + packet_id = P_CLIENTSTARTWATCH; + } +}; + +class Packet_download: public Packet_playerbase { +public: + int seed; + Byte bloc, next, next2, next3, bonus, idle, state; + struct { + Byte x; //position du 'trou' + Byte color; + Byte blind_time; + Word hole_pos; //Hole positions + bool final; + } bon[20]; //les lignes chiantes en attente + Byte can[32][10]; + bool occ[32][10]; + Byte blinded[32][10]; + Byte attacks[MAXPLAYERS]; + Byte last_attacker; + + Packet_download() { + packet_id = P_DOWNLOAD; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_lines: public Packet_playerbase { +public: + Byte nb; + Byte nc; + Byte lx; + Byte sender; + Attack attack; + Word hole_pos[36]; + Packet_lines() { + packet_id = P_LINES; + for(int i=0; i<36; i++) + hole_pos[i]=0; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientlines: public Packet_lines { +public: + Packet_clientlines() { + packet_id = P_CLIENTLINES; + } +}; + +class Packet_testping: public Packet_tcp { +public: + Dword frame; + Packet_testping() { + packet_id = P_TESTPING; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clienttestping: public Packet_testping { +public: + Packet_clienttestping() { + packet_id = P_CLIENTTESTPING; + } +}; + +class Packet_servertestping: public Packet_testping { +public: + Packet_servertestping() { + packet_id = P_SERVERTESTPING; + } +}; + +class Packet_first_frag: public Packet_playerbase { +public: + Packet_first_frag() { + packet_id = P_FIRST_FRAG; + } +}; + +class Packet_clientfirst_frag: public Packet_first_frag { +public: + Packet_clientfirst_frag() { + packet_id = P_CLIENTFIRST_FRAG; + } +}; + +class Packet_gone: public Packet_playerbase { +public: + bool chat_msg; //Inverted on the net + Packet_gone() { + packet_id = P_GONE; + chat_msg = true; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientgone: public Packet_gone { +public: + Packet_clientgone() { + packet_id = P_CLIENTGONE; + } +}; + +class Packet_endgame: public Packet_tcp { +public: + bool auto_end; + Packet_endgame() { + packet_id = P_ENDGAME; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_rejoin: public Packet_playerbase { +public: + int h_repeat, v_repeat, smooth, shadow, handicap; + Packet_rejoin() { + packet_id = P_REJOIN; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_moves: public Packet_playerbase { +public: + Byte size; + Byte moves[256]; + Packet_moves() { + packet_id = P_MOVES; + size=0; + } + void start_byte(); + void set_bit(int v); + void write_byte(); + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientmoves: public Packet_moves { +public: + Packet_clientmoves() { + packet_id = P_CLIENTMOVES; + } +}; + +class Packet_state: public Packet_playerbase { +public: + Byte state; + Packet_state() { + packet_id = P_STATE; + state=255; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_clientstate: public Packet_state { +public: + Packet_clientstate() { + packet_id = P_CLIENTSTATE; + } +}; + +class Packet_serverstate: public Packet_state { +public: + Packet_serverstate() { + packet_id = P_SERVERSTATE; + //We got a Byte player from Packet_state but we don't need it + // so we'll always set it to 0. Yeah it sucks, sue me. + player = 0; + } +}; + +//This should be sent in the dead space between two syncpoints +// because there's no Packet_clientrandom +class Packet_serverrandom: public Packet_tcp { +public: + Dword seed; + Packet_serverrandom() { + packet_id = P_SERVERRANDOM; + seed=0; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_serverpotato: public Packet_tcp { +public: + Byte team; //The team that will now get the potato + Dword potato_lines; //Number of lines to clear + Packet_serverpotato() { + packet_id = P_SERVERPOTATO; + team=255; + potato_lines=0; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_bye: public Packet_tcp { +public: + Packet_bye() { + packet_id = P_BYE; + } +}; + +class Packet_removebonus: public Packet_playerbase { +public: + Packet_removebonus() { + packet_id = P_REMOVEBONUS; + } +}; + +class Packet_clientremovebonus: public Packet_removebonus { +public: + Packet_clientremovebonus() { + packet_id = P_CLIENTREMOVEBONUS; + } +}; + +class Packet_servernameteam: public Packet_tcp { +public: + Byte team; + char name[40]; + Packet_servernameteam() { + packet_id = P_SERVERNAMETEAM; + name[0]=0; + } + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +#endif diff --git a/include/pane.h b/include/pane.h new file mode 100644 index 0000000..428b57a --- /dev/null +++ b/include/pane.h @@ -0,0 +1,377 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PANE +#define _HEADER_PANE + +#include "inter.h" +#include "listbox.h" +#include "overmind.h" +#include "video.h" +#include "zone_list.h" +#include "net_stuff.h" +#include "score.h" +#include "notify.h" +#include "track.h" + +class Multi_player; +class Pane_option; + +class Pane_info { +public: + Font *font2; + Inter *inter; + int x, y, w, h; + Multi_player *mp; + Bitmap *back, *fond, *back_bottom; + Byte quel_pane; + Pane_info(Bitmap *bit, Font *f2, Inter *in, int j, Multi_player *pmp); + virtual ~Pane_info(); +}; + +class Pane: public Zone, public Module, public Zone_list { + TRACKED; + friend class Multi_player; +protected: + Video_bitmap *screen; + bool hiden; + Zone *clicked; + const Pane_info π + bool draw_background,draw_bottom; + void hidecall(Module *m); + void hideexec(Module *m); + void set_net_pane(int i); + void ifdone(); +public: + Pane(const Pane_info &p, bool dback=true, bool dbottom=false); + virtual ~Pane(); + void hide(); + void show(); + virtual void hide_item(); + virtual void show_item(); + virtual void step(); + virtual void draw(); +}; + +class Pane_option: public Pane, public Notifyable { +public: + Zone *player_info, *block_info, *combo_info, *select_scheme; + Zone *chat_window, *server, *quit; + Zone_text_button *player[3]; + Pane_option(const Pane_info &p); + virtual ~Pane_option(); + virtual void init(); + virtual void step(); + virtual void notify(); +}; + +class Pane_singleplayer: public Pane { +public: + Zone *player[3]; + Pane_singleplayer(const Pane_info &p); + virtual void step(); +}; + +class Pane_close: public Pane { +private: + Dword seconds; + Zone *clock; + static bool global_clock_visible; + bool clock_visible; + void update_clock(); + void show_clock(); +protected: + Zone *close; +public: + Pane_close(const Pane_info &p, bool dback=true, bool dbottom=false); + virtual ~Pane_close(); + virtual void step(); + virtual void hide_item(); + virtual void show_item(); + void allow_clock(); +}; + +class Pane_selectscheme: public Pane_close { + Bitmap *bit; + Palette *pal; + Zone *level[10]; + Zone *debugscheme; +public: + Pane_selectscheme(const Pane_info &p); + virtual ~Pane_selectscheme(); + virtual void step(); +}; + +class Pane_playerinfo: public Pane_close, public Notifyable { + Zone_text *player[MAXPLAYERS], *show_button, *auto_button; + int o_show_val; + bool auto_watch; + int show_quoi; + int tagged[4]; + int total0[MAXTEAMS], total1[MAXTEAMS]; + void add_name(Canvas *c, int i, int x2, int y2); + void add_total(int team, int x2, int y2); + int quel_stat() const; + void tag(int q); + void clear_tag(); + void activate_auto_watch(); + void deactivate_auto_watch(); +public: + Pane_playerinfo(const Pane_info &p); + virtual ~Pane_playerinfo(); + void refresh(); + virtual void notify(); + virtual void process(); + virtual void step(); + virtual void draw(); + void auto_watch_closed(); + bool auto_watch_started(); +}; + +class Pane_server: public Pane_close { + class Zone_update_rate: public Zone_text_input { + char port_st[3]; + public: + Zone_update_rate(Inter* in, const Palette &pal, int px, int py, int pw); + void lost_focus(int cancel); + }; + + Zone *drop_player, *drop_connection, *accept_player, *accept_connection; + Zone *test_ping, *check_ip; +public: + Pane_server(const Pane_info &p); + virtual ~Pane_server(); + virtual void step(); +}; + +class Pane_server_drop_player: public Pane_close, public Notifyable { + class List_player: public Listable { + public: + Byte player; + List_player(const char *s, Byte p, Font *f): Listable(s, f) { + player = p; + } + }; + Zone_listbox *list_player; + Zone *b_drop; + int selected_player; +public: + Pane_server_drop_player(const Pane_info &p); + virtual ~Pane_server_drop_player(); + virtual void step(); + virtual void notify(); +}; + +class Pane_server_drop_connection: public Pane_close, public Notifyable { + class List_connection: public Listable { + public: + Net_connection *c; + List_connection(const char *s, Net_connection *p, Font *f=NULL): Listable(s, f) { + c = p; + } + }; + Zone_listbox *list_connection; + Zone *b_drop; + int selected; +public: + Pane_server_drop_connection(const Pane_info &p); + virtual ~Pane_server_drop_connection(); + virtual void step(); + virtual void notify(); +}; + +class Pane_server_ping: public Pane_close, Net_callable { + void send_test(); + Dword last_frame; + int pingtime; + int moyenne, total, nombre; + int test_delay; +public: + Pane_server_ping(const Pane_info &p); + virtual ~Pane_server_ping(); + virtual void step(); + virtual void net_call(Packet *p2); +}; + +class Pane_server_ip: public Pane_close { +public: + Pane_server_ip(const Pane_info &p); +}; + +class Chat_interface: public Zone, Zone_list, public Notifyable { + class Zone_chat_input: public Zone_text_input { + Chat_interface *parent; + public: + Zone_chat_input(Chat_interface *p, const Palette &pal, Inter* in, char* s, int mlen, int px, int py, int pw); + virtual void lost_focus(int cancel); + }; + friend class Zone_chat_input; + + class Zone_to_team: public Zone_state_text { + public: + Zone_to_team(Inter *in, int *val, int px, int py); + virtual void clicked(int quel); + }; + + Zone_chat_input *zinput; + char buf[256]; + bool delete_screen; + int y_offset; + Video_bitmap *screen; + Bitmap *back; + Zone_state_text *z_from; +public: + Chat_interface(Inter *in, const Palette &pal, Bitmap *bit, int px, int py, int pw, int ph, Video_bitmap *scr=NULL); + virtual ~Chat_interface(); + virtual void draw(); + virtual void process(); + virtual void notify(); + void set_screen_offset(int o, Video_bitmap *vb); +}; + +class Pane_scoreboard: public Pane_close, public Notifyable { + Zone_text_button *b_show_frag; + Array zlist_frag; + bool show_frag; + Score score; + Byte potato_team; + Word old_size; +protected: + Word size; + virtual void activate_frag(); + virtual void deactivate_frag(bool temp); + void scoreboard_invisible(); +public: + Pane_scoreboard(const Pane_info &p, bool control_button, bool dback=true, bool dbottom=false); + virtual ~Pane_scoreboard(); + virtual void step(); + virtual void notify(); + virtual void process(); +}; + +class Pane_chat: public Pane_scoreboard { + Chat_interface *chat; + Zone *b_quit; + Word old_y; + virtual void activate_frag(); + virtual void deactivate_frag(bool temp); +public: + Pane_chat(const Pane_info &p); + virtual ~Pane_chat(); + virtual void step(); +}; + +class Bloc; + +class Pane_blockinfo: public Pane_close, public Notifyable { + Bloc *bloc[7]; + int px; + void block_info(Canvas *can, int dx); + int gauche, droite, old_gauche, old_droite; +public: + Pane_blockinfo(const Pane_info &p); + virtual ~Pane_blockinfo(); + virtual void draw(); + virtual void step(); + virtual void notify(); + void add_info(); +}; + +class Pane_comboinfo: public Pane_close, public Notifyable { + void combo_info(Canvas *can, int dx); + int gauche, droite, old_gauche, old_droite; +public: + Pane_comboinfo(const Pane_info &p); + virtual ~Pane_comboinfo(); + virtual void step(); + virtual void notify(); + void add_info(); +}; + +class Pane_playerstartup: public Pane_close, public Notifyable { + Zone_listbox *list_player; + Zone_state_text *list_team; + Zone_state_text *list_handicap; + Zone *b_start, *color_team; + int qplayer; + int color; + int handicap; +public: + Pane_playerstartup(const Pane_info &p, int q); + virtual ~Pane_playerstartup(); + virtual void notify(); + virtual void step(); + void update_player(); +}; + +class Pane_playerjoin: public Pane_close, public Net_callable { + bool got_answer; + int qplayer; + Exec_ping *eping; + Zone_text *status; +public: + Pane_playerjoin(const Pane_info &p, int q); + virtual ~Pane_playerjoin(); + virtual void step(); + virtual void net_call(Packet *p2); +}; + +class Pane_startgame: public Pane_close, public Notifyable { + int qplayer; + int num_player; + bool delete_zone; + void create_zone(); +public: + Canvas *canvas; + Pane_startgame(const Pane_info &p, int q, Canvas *c=NULL, int pos=-1); + virtual ~Pane_startgame(); + virtual void step(); + virtual void notify(); +}; + +class Pane_pre_start: public Pane { + int qplayer; +public: + Pane_pre_start(const Pane_info &p, int q); + virtual ~Pane_pre_start(); + virtual void init(); +}; + +class Watch_canvas: public Zone_list { + bool small_watch; +public: + Canvas *c; + int play, x, y; + Watch_canvas(int player, bool s=false); + void stop(); + void start(); + void small_canvas(const Pane_info &pi, int tx, int ty); +}; + +class Pane_startwatch: public Pane_startgame { + Watch_canvas *watch; + Pane_playerinfo *pinfo; +public: + bool auto_watch; + Pane_startwatch(const Pane_info &p, int player, Pane_playerinfo *ppinfo); + virtual ~Pane_startwatch(); + virtual void step(); + virtual void notify(); +}; + +class Pane_smallwatch: public Pane_scoreboard { + Watch_canvas *watch[4]; + int compte; + Pane_playerinfo *pinfo; +public: + bool auto_watch; + Pane_smallwatch(const Pane_info &p, int tagged[], Pane_playerinfo *ppinfo); + virtual ~Pane_smallwatch(); + virtual void notify(); + virtual void draw(); +}; + +#endif diff --git a/include/player.h b/include/player.h new file mode 100644 index 0000000..25b7b36 --- /dev/null +++ b/include/player.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PLAYER +#define _HEADER_PLAYER + +#include "types.h" + +class Cok { +public: + Dword in_uid; + Dword out_uid; + Dword adr; + Cok(Dword a); +}; + +#endif diff --git a/include/qserv.h b/include/qserv.h new file mode 100644 index 0000000..9b4c067 --- /dev/null +++ b/include/qserv.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_QSERV +#define _HEADER_QSERV + +#include "types.h" + +class Http_post; +class Dict; + +class Qserv { + Http_post *req; + char status[256]; + Dict *reply; +public: + static Dword http_addr; + static int http_port; + Qserv(); + virtual ~Qserv(); + bool done(); + void add_data(const char *s, ...); + void send(); + bool bad_reply(); + const char *get_status(); + Dict *get_reply(); + bool isconnected() const; + Dword getnbrecv() const; +}; + +#endif diff --git a/include/quadra.h b/include/quadra.h new file mode 100644 index 0000000..f91f2ee --- /dev/null +++ b/include/quadra.h @@ -0,0 +1,215 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_QUADRA +#define _HEADER_QUADRA + +#include +#include "overmind.h" +#include "inter.h" +#include "notify.h" +#include "sprite.h" +#include "color.h" +#include "net_call.h" +#include "zone_list.h" + +class Canvas; +class Sample; + +extern Inter* ecran; +extern Overmind overmind; + +extern Color* color[]; +extern Font *fteam[]; + +extern Sprite *cur; + +void raw_draw_bloc(const Video_bitmap* bit, int x, int y, Byte side, Color* col); +void raw_draw_bloc_corner(const Video_bitmap* bit, int x, int y, Byte side, Color* col, Byte to[4]); +void raw_small_draw_bloc(const Video_bitmap* bit, int x, int y, Byte side, Color* col); +void set_fteam_color(const Palette& pal); + +class Zone_combo; +class Zone_list; + +class Player_base: public Module { +protected: + Canvas *canvas; + bool move_left(); + bool move_right(); + bool rotate_left(); + bool rotate_right(bool twice=false); + void move_down(); + void drop_down(); + Byte calc_by(int py) const; + bool check_gone(); + void check_state(); + void remove_bonus(); + void check_bonus(); + void play_sound(Sample *s, int vol, int pan, int freq); +public: + Player_base(Canvas *c) { + canvas = c; + } + virtual void step(); +}; + +class Player_normal: public Player_base { +public: + Player_normal(Canvas *c); + virtual void step(); +}; + +class Player_get_next: public Player_base { + void shift_next(); +public: + Player_get_next(Canvas *c): Player_base(c) { } + virtual void step(); +}; + +class Player_process_key: public Player_base { + int hold_left, hold_right; + Dword last_video_frame, last_overmind_frame; + void keyboard_control(); + void playback_control(); + bool check_first_frag(); + Dword block_rotated; + Dword time_held; +public: + Player_process_key(Canvas *c); + virtual void init(); + virtual void step(); +}; + +class Player_check_line: public Player_base { + int check_nb_line(); + void check_clean(); + bool hole_pos[36][18]; +public: + Player_check_line(Canvas *c); + virtual void step(); +}; + +class Player_text_scroll: public Player_base, public Notifyable { +protected: + Zone_combo *combo; + void stop(); +public: + Player_text_scroll(Canvas *c, const char *texte, int xoffset=4, int yoffset=0); + virtual ~Player_text_scroll(); + virtual void step(); + virtual void notify(); +}; + +class Player_add_bonus: public Player_base { + int nb; + int anim; + bool first_done; +public: + Player_add_bonus(Canvas *c); + virtual ~Player_add_bonus(); + virtual void step(); +}; + +class Player_flash_lines: public Player_base { + int anim; +public: + Player_flash_lines(Canvas *c); + virtual void step(); +}; + +class Player_check_link: public Player_base { + int anim; + void fill_bloc(Byte x, Byte y); + int tombe; +public: + Player_check_link(Canvas *c); + virtual void step(); +}; + +class Player_init: public Player_base, public Net_callable { +public: + Player_init(Canvas *canvas); + virtual void step(); + virtual void net_call(Packet *p2); +}; + +class Player_change_level: public Player_base { +public: + Player_change_level(Canvas *c); + virtual ~Player_change_level(); + virtual void init(); +}; + +class Player_level_up: public Player_base { +public: + Player_level_up(Canvas *c); + virtual void init(); + virtual void step(); +}; + +class Player_dead_wait: public Player_base { + bool add_bonus; + bool check_first_frag(); +public: + Player_dead_wait(Canvas *c, bool ab=true); + virtual void init(); + virtual void step(); +}; + +class Player_first_frag: public Player_base { + int i,j,c; + Byte couleur; +public: + Player_first_frag(Canvas *c); + virtual void step(); +}; + +class Player_dead: public Player_base { + int i,j,c; + Byte couleur; + bool then_gone; +public: + Player_dead(Canvas *c, bool tg=false); + virtual void step(); +}; + +class Packet_moves; + +class Player_wait_block: public Player_base { + bool check_first_frag(); + Dword move_index; +public: + Player_wait_block(Canvas *c); + virtual ~Player_wait_block(); + virtual void init(); + virtual void step(); +}; + +class Packet_stampblock; + +class Player_stamp: public Player_base { + Player_add_bonus* addbonus; + void stamp_bloc(); +public: + Player_stamp(Canvas *c, Packet_stampblock *p); + virtual void init(); +}; + +class Player_wait_respawn: public Player_base { + bool add_bonus; +public: + Player_wait_respawn(Canvas *c, bool ab=true); + virtual void step(); +}; + +class Player_gone: public Player_base { +public: + Player_gone(Canvas *c, bool chat_msg); + virtual void step(); +}; + +#endif diff --git a/include/recording.h b/include/recording.h new file mode 100644 index 0000000..26b3966 --- /dev/null +++ b/include/recording.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RECORDING +#define _HEADER_RECORDING + +#include "types.h" +#include "net_stuff.h" +#include "buf.h" + +class Res_compress; +class Res; +class Canvas; + +class Recording { + TRACKED; + char playername[40]; + int score, lines, level; + /* + Voici les hunks: + 0: start_for_single: un seed pis 3 repeatspeeds + 1: single_start: un numero de player + 2: game_begin: les touches d'une game single + 3: game_stop: les infos d'une game single + 11: write_packet: un numero de frame d'overmind (Dword), la size d'un packet (Word) pis le packet + 13: summary: size (Dword) et du texte (à la Qserv) + */ + void write_hunk(Byte h); + void end_single(Canvas *c); +public: + Byte *all_output; + Dword all_output_size; + Dword frame; + void step(); + Res_compress *res; + Recording(); + virtual ~Recording(); + void start_for_multi(Packet* p); + void write_packet(Packet* p); + bool create(const char *n); + void end_multi(); + void write_summary(); +}; + +class Demo_packet { +public: + Dword frame; + Packet* p; + Demo_packet(const Demo_packet& dp); + Demo_packet(Dword pframe, Packet* pp); + virtual ~Demo_packet(); +}; + +class Dict; + +class Playback { + TRACKED; + Res *res; + Byte read_hunk(); + void read_all(); //Sole caller of next 6 + void read_seed(); + void read_single(); + void read_data(); + void read_info(); + void read_packet(); + void read_summary(); + Buf data; + Dword nextByte; + Array packets; +public: + Byte single_player; + Packet_gameserver *packet_gameserver; + Dict *sum; + bool single(); + bool old_mode; + bool valid; + bool completed; + bool auto_demo; + void create_game(); + void shit_skipper2000(bool remove_chat); + int seed; + struct { + char name[40]; + int color, shadow, smooth, repeat; + } player[3]; + int score, lines, level, multi_level; + Playback(Res* r); + virtual ~Playback(); + Byte get_byte(); + bool check_scores(Canvas* c); + Demo_packet next_packet(); + void remove_packet(); +}; + +extern Recording *recording; +extern Playback *playback; + +#endif diff --git a/include/score.h b/include/score.h new file mode 100644 index 0000000..643d869 --- /dev/null +++ b/include/score.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_SCORE +#define _HEADER_SCORE + +#include "types.h" +#include "stats.h" +#include "net_stuff.h" + +class Dict; + +class Score { + CS::Stat_type current_sort; + void update_team(); +public: + Byte player_team[MAXPLAYERS]; + Byte player_count[MAXTEAMS]; + Byte team_order[MAXTEAMS], order[MAXPLAYERS]; + CS team_stats[MAXTEAMS], stats[MAXPLAYERS]; + bool team_order_changed, order_changed; + bool team_goals_changed[MAXTEAMS]; + Score(); + void reset_order(); + void updateFromGame(); + void updateFromDict(Dict *d); + void sort(CS::Stat_type type); +}; + +#endif diff --git a/include/sons.h b/include/sons.h new file mode 100644 index 0000000..3335464 --- /dev/null +++ b/include/sons.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_SONS +#define _HEADER_SONS + +#include "sound.h" + +typedef struct { + Sample *fadein, *fadeout, *point, *click, *drip, *glass, *depose; + Sample *depose2, *depose3, *depose4, *flash, *levelup, *bonus1, *pause; + Sample *enter, *start, *msg, *potato_get, *potato_rid; +} Samples; + +extern Samples sons; + +#endif diff --git a/include/stats.h b/include/stats.h new file mode 100644 index 0000000..0412b17 --- /dev/null +++ b/include/stats.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_STATS +#define _HEADER_STATS + +#include "types.h" + +class Stat { + int value; +public: + Stat() { + value=0; + } + void add(int n) { + value+=n; + } + int get_value() const { + return value; + } + int *get_address() { + return &value; + } + void set_value(const int i) { + value = i; + } + bool operator<(const Stat& o) const { + return value(const Stat& o) const { + return value>o.value; + } + bool operator!=(const Stat& o) const { + return value!=o.value; + } +}; + +class CS { +public: + enum Stat_type { + //COMPTE0 must be first + COMPTE0, COMPTE1, COMPTE2, COMPTE3, COMPTE4, COMPTE5, + COMPTE6, COMPTETOT, + CLEAR00, CLEAR01, CLEAR02, CLEAR03, CLEAR04, CLEAR05, + CLEAR06, CLEAR07, CLEAR08, CLEAR09, CLEAR10, CLEAR11, + CLEAR12, CLEAR13, CLEARMORE, + DEATH, FRAG, LINESTOT, SCORE, + LAST_BEFORE_120, //Actual value irrelevent + //All new 1.2.0 stats must be after LAST_BEFORE_120 + CLEAR14, CLEAR15, CLEAR16, CLEAR17, CLEAR18, CLEAR19, CLEAR20, + ROTATED0, ROTATED1, ROTATED2, ROTATED3, ROTATED4, ROTATED5, + ROTATED6, ROTATEDTOT, + PLAYING_TIME, PPM, BPM, ROUND_WINS, + SUICIDES, OVERKILLEE, OVERKILLER, MAXOVERKILLEE, MAXOVERKILLER, + COMBO00, COMBO01, COMBO02, COMBO03, COMBO04, COMBO05, + COMBO06, COMBO07, COMBO08, COMBO09, COMBO10, COMBO11, + COMBO12, COMBO13, COMBO14, COMBO15, COMBO16, COMBO17, + COMBO18, COMBO19, COMBO20, + //LAST must be last (duh) + LAST + }; + Stat stats[LAST]; + void add(const CS& other); + void clear(); + bool better(const CS& o, Stat_type type); + static Stat_type clear_trans(int i) { + if(i<=13) + return (Stat_type) ((int) CLEAR00 + i); + else + return (Stat_type) ((int) CLEAR14 + i - 14); + } +}; + +class GS { +public: + enum Stat_type { + PLAYING_TIME, ROUND_NUMBER, + LAST + }; + Stat stats[LAST]; + void clear(); +}; + +#endif diff --git a/include/texte.h b/include/texte.h new file mode 100644 index 0000000..29a53c7 --- /dev/null +++ b/include/texte.h @@ -0,0 +1,447 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_TEXTE +#define _HEADER_TEXTE +#include "stringtable.h" + +#define ST_BASE(x) (stringtable->get(x-1)) +#define ST_PLAYER ST_BASE(1) +#define ST_START ST_BASE(2) +#define ST_QUADRA ST_BASE(3) +#define ST_HEROES ST_BASE(4) +#define ST_SCORE ST_BASE(5) +#define ST_LINES ST_BASE(6) +#define ST_LEVEL ST_BASE(7) +#define ST_GOTHISCORE ST_BASE(8) +#define ST_HIGHESTSCORE ST_BASE(9) +#define ST_CREATEORJOIN ST_BASE(10) +#define ST_CLICKTOCREATE ST_BASE(11) +#define ST_CLICKTOJOIN ST_BASE(12) +#define ST_CLICKTOREFRESH ST_BASE(13) +#define ST_GAMESLIST ST_BASE(14) +#define ST_PLAYERINGAME ST_BASE(15) +#define ST_BACK ST_BASE(16) +#define ST_SELECTCONNECTION ST_BASE(17) +#define ST_LOCALGAME ST_BASE(18) +#define ST_INTERNET ST_BASE(19) +#define ST_SLOW ST_BASE(20) +#define ST_NORMAL ST_BASE(21) +#define ST_FAST ST_BASE(22) +#define ST_PRESSAKEY ST_BASE(23) +#define ST_PRESSLEFT ST_BASE(24) +#define ST_PRESSRIGHT ST_BASE(25) +#define ST_PRESSROTCCW ST_BASE(26) +#define ST_PRESSDOWN ST_BASE(27) +#define ST_PRESSROTCW ST_BASE(28) +#define ST_DEMOQUIT1 ST_BASE(29) +#define ST_DEMOQUIT2_1 ST_BASE(30) +#define ST_DEMOQUIT2_2 ST_BASE(31) +#define ST_DEMOQUIT2_3 ST_BASE(32) +#define ST_DEMOQUIT3 ST_BASE(33) +#define ST_DEMOQUIT4 ST_BASE(34) +#define ST_DEMOQUIT5 ST_BASE(35) +#define ST_DEMOQUIT6 ST_BASE(36) +#define ST_DEMOQUIT7 ST_BASE(37) +#define ST_DEMOQUIT8 ST_BASE(38) +#define ST_DEMOQUIT9 ST_BASE(39) +#define ST_DEMOQUIT10 ST_BASE(40) +#define ST_DEMOQUIT11 ST_BASE(41) +#define ST_DEMOQUIT12 ST_BASE(42) +#define ST_DEMOQUIT13 ST_BASE(43) +#define ST_DEMOQUIT14 ST_BASE(44) +#define ST_DEMOQUIT15 ST_BASE(45) +#define ST_DEMOQUIT16 ST_BASE(46) +#define ST_DEMOQUIT17 ST_BASE(47) +#define ST_HELP1 ST_BASE(48) +#define ST_HELP2 ST_BASE(49) +#define ST_HELP3 ST_BASE(50) +#define ST_HELP4 ST_BASE(51) +#define ST_HELP5 ST_BASE(52) +#define ST_HELP6 ST_BASE(53) +#define ST_HELP7 ST_BASE(54) +#define ST_HELP8 ST_BASE(55) +#define ST_HELP9 ST_BASE(56) +#define ST_HELP10 ST_BASE(57) +#define ST_HELP11 ST_BASE(58) +#define ST_HELP12 ST_BASE(59) +#define ST_HELP13 ST_BASE(60) +#define ST_HELP14 ST_BASE(61) +#define ST_HELP15 ST_BASE(62) +#define ST_HELP16 ST_BASE(63) +#define ST_HELP17 ST_BASE(64) +#define ST_HELP18 ST_BASE(65) +#define ST_HELP19 ST_BASE(66) +#define ST_HELP20 ST_BASE(67) +#define ST_HELP21 ST_BASE(68) +#define ST_HELP22 ST_BASE(69) +#define ST_INTRO1 ST_BASE(70) +#define ST_INTRO2 ST_BASE(71) +#define ST_INTRO3 ST_BASE(72) +#define ST_INTRO4 ST_BASE(73) +#define ST_INTRO5 ST_BASE(74) +#define ST_INTRO6 ST_BASE(75) +#define ST_INTRO7 ST_BASE(76) +#define ST_INTRO8 ST_BASE(77) +#define ST_INTRO9 ST_BASE(78) +#define ST_INTRO10 ST_BASE(79) +#define ST_INTRO11 ST_BASE(80) +#define ST_ENDGAMETITLE ST_BASE(81) +#define ST_FORTHETEAM ST_BASE(82) +#define ST_DEMOSTAT1 ST_BASE(83) +#define ST_DEMOSTAT2 ST_BASE(84) +#define ST_STARTBOB ST_BASE(85) +#define ST_SHOWPLAYER ST_BASE(86) +#define ST_BLOCKINFO ST_BASE(87) +#define ST_LINESINFO ST_BASE(88) +#define ST_SELECTSCHEME ST_BASE(89) +#define ST_CHATWINDOW ST_BASE(90) +#define ST_DEMOSEND ST_BASE(91) +#define ST_CLOSE ST_BASE(92) +#define ST_SENDLINES ST_BASE(93) +#define ST_BOBJOIN ST_BASE(94) +#define ST_TEAM1 ST_BASE(95) +#define ST_TEAM2 ST_BASE(96) +#define ST_TEAM3 ST_BASE(97) +#define ST_TEAM4 ST_BASE(98) +#define ST_TEAM5 ST_BASE(99) +#define ST_TEAM6 ST_BASE(100) +#define ST_TEAM7 ST_BASE(101) +#define ST_BOBDIED ST_BASE(102) +#define ST_GAMESCORE ST_BASE(103) +#define ST_GAMELINES ST_BASE(104) +#define ST_GAMELEVEL ST_BASE(105) +#define ST_GAMEDEATHS ST_BASE(106) +#define ST_GAMEFRAGS ST_BASE(107) +#define ST_CREATEGAMETITLE ST_BASE(108) +#define ST_STARTGAME ST_BASE(109) +#define ST_JOINGAMETITLE ST_BASE(110) +#define ST_CLEARINFO1 ST_BASE(111) +#define ST_CLEARINFO2 ST_BASE(112) +#define ST_CLEARINFO3 ST_BASE(113) +#define ST_CLEARINFO4 ST_BASE(114) +#define ST_CLEARINFO5 ST_BASE(115) +#define ST_CLEARINFO6 ST_BASE(116) +#define ST_CLEARINFO7 ST_BASE(117) +#define ST_CLEARINFO8 ST_BASE(118) +#define ST_CLEARINFO9 ST_BASE(119) +#define ST_CLEARINFO10 ST_BASE(120) +#define ST_CLEARINFO11 ST_BASE(121) +#define ST_CLEARINFO12 ST_BASE(122) +#define ST_CLEARINFO13 ST_BASE(123) +#define ST_CLEARINFO14 ST_BASE(124) +#define ST_CLEARINFOMORE ST_BASE(125) +#define ST_TOTAL ST_BASE(126) +#define ST_CLEARDOUBLE ST_BASE(127) +#define ST_CLEARTRIPLE ST_BASE(128) +#define ST_CLEARQUAD ST_BASE(129) +#define ST_CLEARMORE ST_BASE(130) +#define ST_UNKNOWN ST_BASE(131) +#define ST_SELECTCOLORSCHEME ST_BASE(132) +#define ST_FROMLEVEL ST_BASE(133) +#define ST_SELECTTEAM ST_BASE(134) +#define ST_PLAYERINTEAM ST_BASE(135) +#define ST_BOBTEAM2 ST_BASE(136) +#define ST_NOPLAYERJOINED ST_BASE(137) +#define ST_FROM ST_BASE(138) +#define ST_TO ST_BASE(139) +#define ST_ALLTEAM ST_BASE(140) +#define ST_ENTERADDRESS ST_BASE(141) +#define ST_LOOKINGFORHOST ST_BASE(142) +#define ST_CLICKTOCANCEL ST_BASE(143) +#define ST_ERRORLOOKING ST_BASE(144) +#define ST_ENTERGAMENAME ST_BASE(145) +#define ST_WAITINGTOJOIN ST_BASE(146) +#define ST_ENTERINTERNETPORT ST_BASE(147) +#define ST_WAITJOINGAME ST_BASE(148) +#define ST_JOINREFUSED ST_BASE(149) +#define ST_JOINREFUSED2 ST_BASE(150) +#define ST_WAITJOINGAME2 ST_BASE(151) +#define ST_PAUSEDBYBOB ST_BASE(152) +#define ST_SERVER ST_BASE(153) +#define ST_BOBFRAGBOB ST_BASE(154) +#define ST_GAMEBOBCREATED ST_BASE(155) +#define ST_GAMEBOBJOINED ST_BASE(156) +#define ST_WAITJOINGAME3 ST_BASE(157) +#define ST_WAITJOINGAMEDELAY ST_BASE(158) +#define ST_NETERROR ST_BASE(159) +#define ST_NETERROR2 ST_BASE(160) +#define ST_NETWORKDOWN ST_BASE(161) +#define ST_SENDUDPFAILED ST_BASE(162) +#define ST_JOINGAMEFAILED ST_BASE(163) +#define ST_CREATESERVERFAILED ST_BASE(164) +#define ST_CREATECLIENTFAILED ST_BASE(165) +#define ST_SERVERTOOL ST_BASE(166) +#define ST_DROPPLAYER ST_BASE(167) +#define ST_DROPCONNECTION ST_BASE(168) +#define ST_ACCEPTPLAYER ST_BASE(169) +#define ST_ACCEPTCONNECTION ST_BASE(170) +#define ST_YES ST_BASE(171) +#define ST_NO ST_BASE(172) +#define ST_DROPTHISPLAYER ST_BASE(173) +#define ST_BOBWASDROP ST_BASE(174) +#define ST_PLAYERJOINREFUSED ST_BASE(175) +#define ST_PLAYERJOINREFUSED2 ST_BASE(176) +#define ST_DROPTHISCONNECTION ST_BASE(177) +#define ST_SELECTCDMUSIC ST_BASE(178) +#define ST_CDMUSIC1 ST_BASE(179) +#define ST_CDMUSIC2 ST_BASE(180) +#define ST_CDMUSIC3 ST_BASE(181) +#define ST_GAMEOPTION ST_BASE(182) +#define ST_NOTEPORTNUMBER ST_BASE(183) +#define ST_TUTORIALTITLE ST_BASE(184) +#define ST_TUTORIALNEXT ST_BASE(185) +#define ST_TUTORIALBEGIN ST_BASE(186) +#define ST_JOINDOWNLOADING ST_BASE(346) +#define ST_SHOWWHAT ST_BASE(347) +#define ST_SHOWFRAG ST_BASE(348) +#define ST_SHOWSCORE ST_BASE(349) +#define ST_SHOWLINE ST_BASE(350) +#define ST_SHOWBLOC ST_BASE(351) +#define ST_PLAYERJOINALREADY ST_BASE(352) +#define ST_PLAYERJOINALREADY2 ST_BASE(353) +#define ST_BOBINVALIDBLOCK ST_BASE(354) +#define ST_TESTPING ST_BASE(355) +#define ST_SETUPDATESPEED ST_BASE(356) +#define ST_SETUPDATESPEED2 ST_BASE(357) +#define ST_PINGDELAY ST_BASE(358) +#define ST_PINGMOYEN ST_BASE(359) +#define ST_SHOWMODE ST_BASE(360) +#define ST_SHOWMODEFULL ST_BASE(361) +#define ST_SHOWMODESMALL ST_BASE(362) +#define ST_SHOWSELECTED ST_BASE(363) +#define ST_QUIT ST_BASE(364) +#define ST_CHECKIP ST_BASE(365) +#define ST_CHECKIPTITLE ST_BASE(366) +#define ST_HOSTNAME ST_BASE(367) +#define ST_HOSTLIST ST_BASE(368) +#define ST_IPINFO ST_BASE(369) +#define ST_FASTER ST_BASE(370) +#define ST_INTRO12 ST_BASE(371) +#define ST_OLDERVERSION ST_BASE(372) +#define ST_NEWERVERSION ST_BASE(373) +#define ST_RESUMEBOB ST_BASE(374) +#define ST_YOUFRAGBOB ST_BASE(375) +#define ST_CLEANCANVAS ST_BASE(376) +#define ST_NOTQUADRASERVER ST_BASE(377) +#define ST_NOTQUADRASERVER2 ST_BASE(378) +#define ST_JOINOLDERVERSION ST_BASE(379) +#define ST_JOINOLDERVERSION2 ST_BASE(380) +#define ST_JOINNEWERVERSION ST_BASE(381) +#define ST_JOINNEWERVERSION2 ST_BASE(382) +#define ST_ADDRESSBOOKTITLE ST_BASE(383) +#define ST_CONNECT ST_BASE(384) +#define ST_LOOKINGFORBOB ST_BASE(385) +#define ST_SELECTGAMETYPE ST_BASE(386) +#define ST_GAMETYPE1 ST_BASE(387) +#define ST_GAMETYPE2 ST_BASE(388) +#define ST_GAMETYPE2DESC ST_BASE(389) +#define ST_GAMETYPE1DESC ST_BASE(390) +#define ST_GAMESPEED ST_BASE(391) +#define ST_GAMELEVELUP1 ST_BASE(392) +#define ST_GAMELEVELUP2 ST_BASE(393) +#define ST_GAMELEVELSTART ST_BASE(394) +#define ST_BOBWINFIRSTFRAG ST_BASE(395) +#define ST_CMDLINE ST_BASE(396) +#define ST_CMDLINE1 ST_BASE(397) +#define ST_CMDLINE2 ST_BASE(398) +#define ST_CMDLINE3 ST_BASE(399) +#define ST_CMDLINE4 ST_BASE(400) +#define ST_CMDLINE5 ST_BASE(401) +#define ST_CMDLINE6 ST_BASE(402) +#define ST_CMDLINE7 ST_BASE(403) +#define ST_RESULTFRAG ST_BASE(404) +#define ST_RESULTDEATH ST_BASE(405) +#define ST_PLAYBACK ST_BASE(406) +#define ST_SLOWPLAY ST_BASE(407) +#define ST_FASTPLAY ST_BASE(408) +#define ST_GAMEMINIMUM ST_BASE(409) +#define ST_CMDLINE8 ST_BASE(410) +#define ST_RESTART ST_BASE(411) +#define ST_BOBHASGONE ST_BASE(412) +#define ST_GAMEWILLSTART ST_BASE(413) +#define ST_GAMESTARTNOW ST_BASE(414) +#define ST_SERVERDECONNECT ST_BASE(415) +#define ST_REPLAYLASTGAME ST_BASE(416) +#define ST_STOPGAME ST_BASE(417) +#define ST_GAMEEND ST_BASE(418) +#define ST_PLAYERJOINREFUSED3 ST_BASE(419) +#define ST_PLAYERJOINREFUSED4 ST_BASE(420) +#define ST_SETGAMEEND ST_BASE(421) +#define ST_GAMEEND1 ST_BASE(422) +#define ST_GAMEEND2 ST_BASE(423) +#define ST_GAMEEND3 ST_BASE(424) +#define ST_GAMEENDFRAG ST_BASE(425) +#define ST_GAMEENDMINUTE ST_BASE(426) +#define ST_SETMOUSESPEED ST_BASE(427) +#define ST_ADDRESSBOOK ST_BASE(428) +#define ST_REFRESHINTERNET ST_BASE(429) +#define ST_RECEIVINGINTERNET ST_BASE(430) +#define ST_ALLOWPUBLICGAME ST_BASE(431) +#define ST_SAVESETTING ST_BASE(432) +#define ST_SETGAMESERVER ST_BASE(433) +#define ST_ADVANCEDOPTION ST_BASE(434) +#define ST_INVALIDSERVERADDRESS ST_BASE(435) +#define ST_INVALIDSERVERADDRESS2 ST_BASE(436) +#define ST_DEFAULTGAMESERVER ST_BASE(437) +#define ST_QUADRAVERSION ST_BASE(438) +#define ST_NOTREGISTERED ST_BASE(439) +#define ST_NOSERVERFOUND ST_BASE(440) +#define ST_NOSERVERFOUND2 ST_BASE(441) +#define ST_INVALIDSERVERRESPONSE ST_BASE(442) +#define ST_GAMENOTPUBLIC ST_BASE(443) +#define ST_NETWORKERRORLOOKINGBOB ST_BASE(444) +#define ST_REGISTER1 ST_BASE(445) +#define ST_REGISTER2 ST_BASE(446) +#define ST_REGISTER3 ST_BASE(447) +#define ST_REGISTER4 ST_BASE(448) +#define ST_REGISTER5 ST_BASE(449) +#define ST_REGISTER6 ST_BASE(450) +#define ST_REGISTER7 ST_BASE(451) +#define ST_REGISTER8 ST_BASE(452) +#define ST_REGISTER9 ST_BASE(453) +#define ST_REGISTER10 ST_BASE(454) +#define ST_CLICKREGISTER ST_BASE(455) +#define ST_THANKSREGISTER ST_BASE(456) +#define ST_SELECTLANGUAGE ST_BASE(457) +#define ST_ENGLISH ST_BASE(458) +#define ST_FRENCH ST_BASE(459) +#define ST_CREDIT1 ST_BASE(460) +#define ST_CREDIT2 ST_BASE(461) +#define ST_NAME ST_BASE(462) +#define ST_PASSWORD ST_BASE(463) +#define ST_PASSWORDINVALID ST_BASE(464) +#define ST_SECONDSREMAINING ST_BASE(465) +#define ST_NEXTPAGE ST_BASE(466) +#define ST_COMMANDLINEPARAM ST_BASE(467) +#define ST_CMDLINE9 ST_BASE(468) +#define ST_CMDLINE10 ST_BASE(469) +#define ST_UPDATINGGAMESERVER ST_BASE(470) +#define ST_ONEMOMENTPLEASE ST_BASE(471) +#define ST_CMDLINE11 ST_BASE(472) +#define ST_CMDLINE12 ST_BASE(473) +#define ST_CMDLINE13 ST_BASE(474) +#define ST_GLOBALHIGH ST_BASE(475) +#define ST_SYNC ST_BASE(476) +#define ST_CONNECTFROMBOB ST_BASE(477) +#define ST_DISCONNECTFROMBOB ST_BASE(478) +#define ST_HIGHSTATUSNORMAL ST_BASE(479) +#define ST_HIGHSTATUSCONNECTING ST_BASE(480) +#define ST_HIGHSTATUSABORTED ST_BASE(481) +#define ST_HIGHSTATUSCOMPLETED ST_BASE(482) +#define ST_HIGHSTATUSRECEIVING ST_BASE(483) +#define ST_HIGHSTATUSCANCELED ST_BASE(484) +#define ST_HIGHBADSERVER ST_BASE(485) +#define ST_GAMENONAME ST_BASE(486) +#define ST_GAMESTATUS ST_BASE(487) +#define ST_GAMESTATUSNOT ST_BASE(488) +#define ST_GAMESTATUSPLAY ST_BASE(489) +#define ST_GAMESTATUSTERM ST_BASE(490) +#define ST_LOCALNETWORK ST_BASE(491) +#define ST_CMDLINE14 ST_BASE(492) +#define ST_CMDLINE15 ST_BASE(493) +#define ST_CMDLINE16 ST_BASE(494) +#define ST_BOBREJOIN ST_BASE(495) +#define ST_DOWNLOADDECONNECT1 ST_BASE(496) +#define ST_DOWNLOADDECONNECT2 ST_BASE(497) +#define ST_STARTPLAYBACK ST_BASE(498) +#define ST_DEMOCENTRAL ST_BASE(499) +#define ST_CURRENTDIRECTORY ST_BASE(500) +#define ST_DEMOSIZE ST_BASE(501) +#define ST_DEMODATE ST_BASE(502) +#define ST_DEMOVERSION ST_BASE(503) +#define ST_DEMODURATION ST_BASE(504) +#define ST_DEMOTYPE ST_BASE(505) +#define ST_DEMOEND ST_BASE(506) +#define ST_DEMOBADFILE ST_BASE(507) +#define ST_DEMOSINGLE ST_BASE(508) +#define ST_DEMOUNKNOWN ST_BASE(509) +#define ST_DEMONEVER ST_BASE(510) +#define ST_DEMOAFTERFRAG ST_BASE(511) +#define ST_DEMOAFTERMINUTE ST_BASE(512) +#define ST_PLAYERS ST_BASE(513) +#define ST_DEMOCOMPLETED ST_BASE(514) +#define ST_RECORDGAME ST_BASE(515) +#define ST_GAMENOTRECORDEDAS ST_BASE(516) +#define ST_GAMERECORDINGAS ST_BASE(517) +#define ST_DEMONAME ST_BASE(518) +#define ST_COLORTEAM ST_BASE(519) +#define ST_BOBMINUTES ST_BASE(520) +#define ST_BOBSECONDS ST_BASE(521) +#define ST_AUTOWATCH ST_BASE(522) +#define ST_PLAYERJOINFULL1 ST_BASE(523) +#define ST_PLAYERJOINFULL2 ST_BASE(524) +#define ST_BOBTEAM ST_BASE(525) +#define ST_FRAGSREMAINING ST_BASE(526) +#define ST_GAMETIED ST_BASE(527) +#define ST_1FRAGREMAINING ST_BASE(528) +#define ST_BOBCLEANBOBLINES ST_BASE(529) +#define ST_BOBCLEAN1LINE ST_BASE(530) +#define ST_GAMETYPE3 ST_BASE(531) +#define ST_GAMETYPE3DESC ST_BASE(532) +#define ST_CMDLINE17 ST_BASE(533) +#define ST_CMDLINE18 ST_BASE(534) +#define ST_CMDLINE19 ST_BASE(535) +#define ST_SELECTHANDICAP ST_BASE(536) +#define ST_BEGINNER ST_BASE(537) +#define ST_APPRENTICE ST_BASE(538) +#define ST_INTERMEDIATE ST_BASE(539) +#define ST_MASTER ST_BASE(540) +#define ST_GRANDMASTER ST_BASE(541) +#define ST_DELETEDEMO ST_BASE(542) +#define ST_WINSGAME ST_BASE(543) +#define ST_GETSPOTATO ST_BASE(544) +#define ST_CLEARBOBLINES ST_BASE(545) +#define ST_GAMETYPE4 ST_BASE(546) +#define ST_GAMEEND4 ST_BASE(547) +#define ST_GAMETYPE4DESC ST_BASE(548) +#define ST_GAMEENDSCORE ST_BASE(549) +#define ST_GAMEENDLINES ST_BASE(550) +#define ST_DEMOAFTERSCORE ST_BASE(551) +#define ST_DEMOAFTERLINES ST_BASE(552) +#define ST_FRAG ST_BASE(553) +#define ST_POINT ST_BASE(554) +#define ST_LINE ST_BASE(555) +#define ST_BOBBOBSREMAINING ST_BASE(556) +#define ST_GAMEEND5 ST_BASE(557) +#define ST_CLEANBOBCLEARSBOBLINES ST_BASE(558) +#define ST_CLEANBOBCLEARS1LINE ST_BASE(559) +#define ST_BOBCLEARSBOBLINEBOB ST_BASE(560) +#define ST_DONEPOTATO ST_BASE(561) +#define ST_RESTARTGAME ST_BASE(562) +#define ST_REJOINGAME ST_BASE(563) +#define ST_GOAL ST_BASE(564) +#define ST_YOUGOTPOTATO1 ST_BASE(565) +#define ST_YOUGOTPOTATO2 ST_BASE(566) +#define ST_YOUGOTRIDOFPOTATO1 ST_BASE(567) +#define ST_YOUGOTRIDOFPOTATO2 ST_BASE(568) +#define ST_SLOG ST_BASE(569) +#define ST_WSLOG ST_BASE(570) +#define ST_GAMETYPEDESCRIPTIONS ST_BASE(571) +#define ST_GAMETYPE5 ST_BASE(572) +#define ST_WAITINGFORROUND1 ST_BASE(573) +#define ST_WAITINGFORROUND2 ST_BASE(574) +#define ST_WAITINGFORKEY1 ST_BASE(575) +#define ST_WAITINGFORKEY2 ST_BASE(576) +#define ST_BOBWINSROUND ST_BASE(577) +#define ST_NOBODY ST_BASE(578) +#define ST_GAMENOTLOGGEDAS ST_BASE(579) +#define ST_GAMELOGGINGAS ST_BASE(580) +#define ST_SHOWPPM ST_BASE(581) +#define ST_SHOWBPM ST_BASE(582) +#define ST_PRESS2ROT ST_BASE(583) +#define ST_PRESSDROP ST_BASE(584) +#define ST_GAMETYPE6 ST_BASE(585) +#define ST_SELECTSINGLEGAMETYPE ST_BASE(586) +#define ST_SINGLENORMAL ST_BASE(587) +#define ST_SINGLESPRINT ST_BASE(588) +#define ST_AVGCLEAR ST_BASE(589) +#define ST_AVGCOMBO ST_BASE(590) +#define ST_RANK ST_BASE(591) +#define ST_SPEED ST_BASE(592) +#define ST_COMBO ST_BASE(593) + +#endif diff --git a/include/zone.h b/include/zone.h new file mode 100644 index 0000000..f419757 --- /dev/null +++ b/include/zone.h @@ -0,0 +1,166 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_ZONE +#define _HEADER_ZONE + +#include "array.h" +#include "types.h" +#include "global.h" +#include "notify.h" +#include "bitmap.h" +#include "zone_list.h" +#include "listbox.h" + +class Canvas; +class Bloc; + +class Zone_menu: public Zone_bitmap { + int del_bit; +public: + Zone_menu(Inter* in, Bitmap* bit, int px, int py, Bitmap* bit2); + Zone_menu(Inter* in, const char* b1, int px, int py, const char* b2); + Zone_menu(Inter* in, Bitmap* fond, const char* b1, int px, int py); + Zone_menu(Inter* in, const Image& b1, int px, int py, const Image& b2); + virtual ~Zone_menu(); + virtual void entered(); + virtual void clicked(int quel); +}; + +class Zone_text_select2: public Zone_text_select { +public: + Zone_text_select2(Inter* in, Font* f, const char* s, int px, int py): + Zone_text_select(in, f, s, px, py) { } + Zone_text_select2(Inter* in, Font* f, const char* s, int px, int py, int pw): + Zone_text_select(in, f, s, px, py, pw) { } + virtual void entered(); + virtual void clicked(int quel); +}; + +class Zone_text_button2: public Zone_text_button { +public: + Zone_text_button2(Inter* in, Bitmap *b, Font* f, const char* s, int px, int py): + Zone_text_button(in, b, f, s, px, py) { } + Zone_text_button2(Inter* in, Bitmap *b, Font* f, const char* s, int px, int py, int pw): + Zone_text_button(in, b, f, s, px, py, pw) { } + Zone_text_button2(Inter* in, Bitmap* b, Font* f, const char* s, int py): + Zone_text_button(in, b, f, s, py) { } + virtual void entered(); + virtual void clicked(int quel); +}; + +class Zone_listbox2: public Zone_listbox { +public: + Zone_listbox2(Inter* in, Bitmap *fond, Font *f, int *pval, int px, int py, int pw, int ph): + Zone_listbox(in, fond, f, pval, px, py, pw, ph) { } + virtual void clicked(int quel); +}; + +class Zone_state_text2: public Zone_state_text, public Observable { +public: + Zone_state_text2(Inter* in, int *pval, int px, int py, int pw=50, int ph=20): + Zone_state_text(in, pval, px, py, pw, ph) { } + virtual void clicked(int quel); +}; + +class Zone_color_select: public Zone_state { + Byte col[MAXTEAMS]; +public: + Zone_color_select(Inter* in, int *pv, int px, int py, Byte co[MAXTEAMS]); + virtual void draw(); +}; + +class Zone_color_select_noclick: public Zone_color_select { +public: + Zone_color_select_noclick(Inter* in, int *pv, int px, int py, Byte co[MAXTEAMS]); + virtual void clicked(int quel) { } +}; + +class Zone_set_key: public Zone_state_text { +public: + Zone_set_key(Inter* in, int *pv, int px, int py); + virtual void clicked(int quel) { } +}; + +class Zone_next: public Zone { +protected: + Bitmap* back; + Bloc* next; +public: + Zone_next(Inter* in, const Bitmap& fond, int px, int py, int pw=4*18, int ph=2*18); + virtual ~Zone_next(); + void set_next(Bloc* n); + virtual void draw(); +}; + +class Zone_small_next: public Zone_next { +public: + Zone_small_next(Inter* in, const Bitmap& fond, int px, int py); + virtual void draw(); +}; + +class Zone_combo: public Zone_text { + Canvas *canvas; + Zone_combo **zone; +public: + Zone_combo(Zone_combo **z, Canvas *c, const char *text, int px, int py); + virtual ~Zone_combo(); + virtual void draw(); +}; + +class Zone_bonus: public Zone_watch_int { +protected: + Canvas *canvas; + Bitmap *back; +public: + Zone_bonus(Inter* in, int px, int py, int *v, Canvas *c, const Bitmap& bit, int pw=18, int ph=18*20); + virtual ~Zone_bonus(); + virtual void draw(); +}; + +class Zone_canvas: public Zone { +protected: + Bitmap *fond; + Video_bitmap *screen; + Canvas *canvas; + Zone_next *znext, *znext2, *znext3; + Zone *zbonus; +public: + Zone_canvas(Inter* in, Bitmap& bit, int px, int py, Canvas *can, int pw=18*10, int ph=18*20, bool small_watch=false); + virtual ~Zone_canvas(); + virtual void draw(); +}; + +class Zone_canvas_bloc: public Zone { +protected: + Canvas *canvas; +public: + Zone_canvas_bloc(Inter* in, Canvas *can): Zone(in) { + canvas = can; + } + virtual void draw(); +}; + +class Zone_small_canvas: public Zone_canvas { +public: + Zone_small_canvas(Inter* in, Bitmap& bit, int px, int py, Canvas *can); + virtual void draw(); +}; + +class Zone_small_canvas_bloc: public Zone_canvas_bloc { +public: + Zone_small_canvas_bloc(Inter* in, Canvas *can): Zone_canvas_bloc(in, can) { + } + virtual void draw(); +}; + +class Zone_small_bonus: public Zone_bonus { +public: + Zone_small_bonus(Inter* in, int px, int py, int *v, Canvas *c, const Bitmap& bit); + virtual void draw(); +}; + +#endif diff --git a/include/zone_list.h b/include/zone_list.h new file mode 100644 index 0000000..d4f972e --- /dev/null +++ b/include/zone_list.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_ZONE_LIST +#define _HEADER_ZONE_LIST + +#include "array.h" +#include "types.h" +#include "inter.h" + +class Zone_list { +protected: + Array zone; + void deleteall(); + virtual ~Zone_list() { + deleteall(); + } +}; + +#endif diff --git a/packages/dir.mk b/packages/dir.mk new file mode 100644 index 0000000..3679d30 --- /dev/null +++ b/packages/dir.mk @@ -0,0 +1,54 @@ +# Makefile pour Quadra +# $Id$ + +ifndef MAKEFILE_INCLUDED + +.PHONY: dist + +dist: + $(MAKE) -C .. dist + +else + +MAJOR:=$(shell grep 'Config::major' source/config.cpp | cut -d= -f2 | bc) +MINOR:=$(shell grep 'Config::minor' source/config.cpp | cut -d= -f2 | bc) +PATCHLEVEL:=$(shell grep 'Config::patchlevel' source/config.cpp | cut -d= -f2 | bc) +VERSION:=$(MAJOR).$(MINOR).$(PATCHLEVEL) +QUADRADIR:=quadra-$(VERSION) +QUADRAPKG:=$(QUADRADIR).tar.gz + +PACKAGE_DEPS:=source/quadra source/quadra.res packages/install.sh history.txt license.txt packages/README packages/quadra.spec images/quadra.xpm + +TARGETS+= +CLEANS+=packages/README packages/quadra.spec $(QUADRADIR) $(QUADRAPKG) + +dist: + @$(MAKE) $(QUADRAPKG) RELEASE=yes + +$(QUADRAPKG): $(QUADRADIR) $(QUADRADIR)/* $(PACKAGES_DEPS) + tar cf - $< | gzip -9 > $@ + +$(QUADRADIR): $(PACKAGE_DEPS) + rm -rf $@ + mkdir $@ + cp $^ $@ + mv $@/history.txt $@/ChangeLog + mv $@/license.txt $@/LICENSE + chmod 755 $@/install.sh + +packages/README: packages/readme.txt + sed $^ -e 's/@VERSION@/$(VERSION)/g' > $@ + +packages/quadra.spec: packages/quadra.spec.in + sed $^ -e 's/@VERSION@/$(VERSION)/g' > $@ + +endif + +#dist: ../source/quadra ../source/quadra.res install.sh readme.txt +# +#cleanhere: +# +#ROOTDIR=.. +#include ../Config.make +#include ../Rules.make +# diff --git a/packages/install.sh b/packages/install.sh new file mode 100755 index 0000000..4737f39 --- /dev/null +++ b/packages/install.sh @@ -0,0 +1,28 @@ +#! /bin/sh + +VERSION=1.1.1 +PREFIX="" + +if [ $(id -u) -ne 0 ]; then + echo + echo "You must be 'root' to install Quadra." + echo + echo "Use the 'su' command or contact your system administrator." + echo + exit +fi + +echo +echo "Installing Quadra $VERSION..." +echo +echo "Installing quadra -> $PREFIX/usr/games/quadra" +mkdir -p $PREFIX/usr/games +install -s -m 4711 quadra $PREFIX/usr/games/quadra +echo "Installing quadra.res -> $PREFIX/usr/lib/games/quadra.res" +mkdir -p $PREFIX/usr/lib/games +install -m 644 quadra.res $PREFIX/usr/lib/games/quadra.res +echo +echo "Installation is finished!" +echo +echo "To run the game, type $PREFIX/usr/games/quadra" +echo diff --git a/packages/quadra.spec.in b/packages/quadra.spec.in new file mode 100644 index 0000000..d1d8ea5 --- /dev/null +++ b/packages/quadra.spec.in @@ -0,0 +1,51 @@ +Summary: Multiplayer puzzle game +Name: quadra +Version: @VERSION@ +Release: 1 +Copyright: Commercial +Group: Games +URL: http://ludusdesign.com/ +Source: quadra-%{version}.tar.gz +Vendor: Ludus Design +BuildRoot: /var/tmp/quadra-root + +%description +This is the official release %{version} of Quadra, a full-featured +multiplayer action puzzle game for the X Window System and Svgalib. +Features include: + + - Recursive block chaining + - Blocks shadows + - Teams playing + - TCP/IP networking (free Internet playing! Supports SOCKS proxies) + - Smooth block falling + - Sound effects + - Watches on other players + - Chat window + - CD-based music + - And much more! + +The license is based on the shareware model. You can freely download +and copy the game. After a 30 day period, you must register or stop +using it. + +%prep +%setup -q + +%build +echo Binary release, nothing to build. + +%install +mkdir -p $RPM_BUILD_ROOT/usr/games +mkdir -p $RPM_BUILD_ROOT/usr/lib/games +install quadra $RPM_BUILD_ROOT/usr/games/quadra +install quadra.res $RPM_BUILD_ROOT/usr/lib/games/quadra.res + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%attr(4711,root,root) /usr/games/quadra +%attr(0644,root,root) /usr/lib/games/quadra.res +%doc ChangeLog LICENSE README quadra.xpm diff --git a/packages/readme.txt b/packages/readme.txt new file mode 100644 index 0000000..7797121 --- /dev/null +++ b/packages/readme.txt @@ -0,0 +1,60 @@ + +Quadra version @VERSION@ +Ludus Design +http://ludusdesign.com/ + +Description +----------- + +This is the official release @VERSION@ of Quadra, a full-featured +multiplayer action puzzle game for the X Window System and Svgalib. +Features include: + + - Recursive block chaining + - Block shadow + - Team play + - TCP/IP networking (free Internet play! Supports SOCKS proxies) + - Smooth block falling + - Sound effects + - Watches on other players + - Chat window + - CD-based music + - And much more! + +It is based on the shareware model. You can freely download and copy +the game. After a 30 day period, you must register or stop using it. + +Installation +------------ + +Run the install.sh shell script as the root user, like this: + +cd quadra-@VERSION@ +su +sh install.sh + +If you do not have root access on your machine, you can still install +Quadra in your home directory. In this case, you have to set the +QUADRADIR environment variable to the directory that contains the +'quadra.res' resource file. For example: + +export QUADRADIR=$HOME/quadra-@VERSION@ (for a Bourne shell like bash) + +or + +setenv QUADRADIR $HOME/quadra-@VERSION@ (for a C shell like csh) + +You can also transform the quadra-@VERSION@.tar.gz package into a RPM +package. You need a working RPM environment (the root user on a Red +Hat system already has such an environment), then it is simply a +matter of entering this command: + +rpm -tb quadra-@VERSION@.tar.gz + +For further support questions, refer to the support section of the +Ludus Design web site at http://ludusdesign.com/support.html or e-mail +us at support@ludusdesign.com, including all relevant information, +like error messages, computer model, video and sound card models and +operating system version. + +Have fun playing Quadra! diff --git a/res.bat b/res.bat new file mode 100755 index 0000000..b5f734d --- /dev/null +++ b/res.bat @@ -0,0 +1 @@ +@skelton\tools\wadder\debug\wadder quadra.res resources.txt diff --git a/resources.txt b/resources.txt new file mode 100644 index 0000000..9650097 --- /dev/null +++ b/resources.txt @@ -0,0 +1,140 @@ +sons/zingle.wav +sons/potato_get.wav +sons/tapdrip.wav +sons/handbell.wav +images/setup.raw +images/setup0.raw +images/setup1.raw +images/setup2.raw +images/setup3.raw +images/setup4.raw +images/setup5.raw +images/setup6.raw +images/setup7.raw +images/setup8.raw +images/setup9.raw +images/setup10.raw +images/setup11.raw +images/setup12.raw +images/setup13.raw +images/setup14.raw +images/setup15.raw +images/setupf.raw +images/setupf0.raw +images/setupf1.raw +images/setupf2.raw +images/setupf3.raw +images/setupf4.raw +images/setupf5.raw +images/setupf6.raw +images/setupf7.raw +images/setupf8.raw +images/setupf9.raw +images/setupf10.raw +images/setupf11.raw +images/setupf12.raw +images/setupf13.raw +images/setupf14.raw +images/setupf15.raw +sons/click_1.wav +images/multi.raw +sons/glass01.wav +fonts/font.fnt +fonts/courrier.fnt +sons/fadeout.wav +sons/w_bayo_0.wav +images/cursor.raw +sons/blip1.wav +sons/metal3.wav +sons/chop2.wav +sons/hitwood1.wav +sons/pwap2.wav +sons/clang3.wav +images/gamelvup.raw +sons/whizz1.wav +sons/glissup.wav +sons/cuckoo.wav +images/gamepaus.raw +images/hscore1.raw +images/hscore1s.raw +images/hscore2s.raw +images/hscore.raw +images/hscoretf.raw +images/hscore1f.raw +images/hscor1fs.raw +images/hscore2f.raw +images/hscor2fs.raw +sons/knock.wav +sons/click_3.wav +sons/metal6.wav +sons/metal1.wav +images/fond0.pcx +images/fond1.pcx +images/fond2.pcx +images/fond3.pcx +images/fond4.pcx +images/fond5.pcx +images/fond6.pcx +images/fond7.pcx +images/fond8.pcx +images/fond9.pcx +sons/whistle1.wav +sons/whistle2.wav +sons/click01.wav +sons/spring.wav +sons/pop1.wav +sons/splodge.wav +sons/bloop.wav +sons/corkpop.wav +sons/glass03.wav +sons/glass04.wav +sons/explod06.wav +sons/explod05.wav +sons/explod03.wav +sons/ceramic3.wav +sons/water05_3.wav +sons/water05_1.wav +sons/water05_2.wav +sons/bubble2.wav +sons/smash2.wav +images/debuto.raw +images/debut0.raw +images/debut1.raw +images/debut2.raw +images/debut3.raw +images/debut4.raw +images/debut5.raw +images/debut6.raw +images/debut7.raw +images/debut8.raw +images/debutof.raw +images/debut0f.raw +images/debut1f.raw +images/debut2f.raw +images/debut3f.raw +images/debut4f.raw +images/debut5f.raw +images/debut6f.raw +images/debut7f.raw +images/debutnr.raw +textes/anglais.txt +textes/francais.txt +textes/help_en.txt +textes/help_fr.txt +textes/gdesc_en.txt +textes/gdesc_fr.txt +images/hscore2.raw +images/result.raw +images/resultf.raw +images/game_1.raw +images/game_2.raw +images/game_3.raw +images/game_4.raw +images/game_5.raw +sons/hooter03.wav +images/black.pcx +demos/demo00.rec +demos/demo01.rec +demos/demo02.rec +demos/demo03.rec +images/xlogo.raw diff --git a/server/qserv.pl b/server/qserv.pl new file mode 100644 index 0000000..cba692d --- /dev/null +++ b/server/qserv.pl @@ -0,0 +1,551 @@ +#! /usr/bin/perl -w -T + +use CGI; +use Data::Dumper; +use DB_File; +use Fcntl qw(:flock); + +$TIMEOUT = 180; +$DEFAULTDEMOS = 5; +$MAXDEMOS = 100; + +sub superuser { + my($params) = shift; + if($params->{info}{remoteaddr} eq '127.0.0.1') { + return 1; + } + return 0; +} + +%commands = ( + 'ping' => + sub { + my($params) = shift; + my($ret) = "ping\n" . &format($params); + return $ret; + }, + + 'getgames' => + sub { + my($params) = shift; + my($ret); + my($db) = &allocdb("games.dbm", LOCK_SH); + my(%sent); + $ret = "Current games\n"; + while(($key, $val) = each(%$db)) { + my($data) = eval($val); + if(time - $data->{info}{lastupdate} <= $TIMEOUT) { + if(defined $params->{info}{quadra_version}) { + if($params->{info}{quadra_version} eq "1.1.2") { + delete $data->{players}; + } + } + else { + if(!defined $params->{info}{qsnoop_version}) { + $key =~ s/:.*//; + } + } + if(!defined $sent{$key}) { + $ret .= &format($data, $key); + } + $sent{$key} = 1; + } + } + &freedb($db); + return $ret; + }, + + 'getgameshtml' => + sub { + my($params) = shift; + my($ret); + my(%colors) = ( + '0' => '#F06000', + '1' => '#00D0D0', + '2' => '#D00000', + '3' => '#D000D0', + '4' => '#D0D000', + '5' => '#00D000', + '6' => '#0000D0', + ); + my($db) = &allocdb("games.dbm", LOCK_SH); + $ret = "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + while(($key, $val) = each(%$db)) { + my($data) = eval($val); + if(time - $data->{info}{lastupdate} <= $TIMEOUT) { + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + } + } + &freedb($db); + $ret .= "
Game nameIPPlayers
$data->{name}$data->{info}{remoteaddr}\n"; + while(($key, $val) = each(%{$data->{players}})) { + $ret .= "{team}}\">\n"; + $ret .= "$val->{name}\n\n"; + $ret .= "
\n"; + } + $ret .= "
\n"; + return $ret; + }, + + 'postgame' => + sub { + my($params) = shift; + my($ret); + my($db) = &allocdb("games.dbm", LOCK_EX); + if(!defined $params->{port}) { + $params->{port} = 3456; + } + my($addr) = $params->{info}{remoteaddr} . ":" . $params->{port}; + $params->{info}{lastupdate} = time; + if(exists $db->{$addr}) { + $ret = "Game updated\n"; + } + else { + $ret = "Game added\n"; + } + $db->{$addr} = Dumper($params); + &freedb($db, 1); + return $ret; + }, + + 'deletegame' => + sub { + my($params) = shift; + my($ret); + my($db) = &allocdb("games.dbm", LOCK_EX); + if(!defined $params->{port}) { + $params->{port} = 3456; + } + my($addr) = $params->{info}{remoteaddr} . ":" . $params->{port}; + if(exists $db->{$addr}) { + delete $db->{$addr}; + $ret = "Game deleted\n"; + } + else { + $ret = "Game not found\n"; + } + &freedb($db, 1); + return $ret; + }, + + 'deleteoldgames' => + sub { + my($params) = shift; + my($ret); + if(&superuser($params)) { + my($db) = &allocdb("games.dbm", LOCK_EX); + $ret = "Deleded games\n"; + my(@reaplist); + while(($key, $val) = each(%$db)) { + my($game) = eval($val); + if(time - $game->{info}{lastupdate} > $TIMEOUT) { + push @reaplist, $key; + $ret .= &format($game, $key); + } + } + foreach $key (@reaplist) { + delete $db->{$key}; + } + &freedb($db, 1); + } + else { + $ret = "Permission denied\n"; + } + return $ret; + }, + + 'gethighscores' => + sub { + my($params) = shift; + my($db) = &allocdb("highscores.dbm", LOCK_SH); + my($ret) = "Ok\n"; + $ret .= &format(&getscores($db, $params->{num})); + &freedb($db); + return $ret; + }, + + 'gethighscoreshtml' => + sub { + my($params) = shift; + my($db) = &allocdb("highscores.dbm", LOCK_SH); + $scores = &getscores($db, $params->{num}); + &freedb($db); + $ret = "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + my($i); + for($i=0; ; $i++) { + my($high) = sprintf "high%03i", $i; + last unless exists $scores->{$high}; + my($data) = $scores->{$high}; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + $ret .= "\n"; + } + $ret .= "
PositionPlayer nameScoreLinesLevelDate
"; + $ret .= $i+1; + $ret .= "$data->{name}$data->{score}$data->{lines}$data->{level}"; + my($sec, $min, $hour, $mday, $mon, $year, + $wday, $yday, $isdst) = localtime $data->{date}; + $ret .= sprintf "%i-%02i-%02i %02i:%02i:%02i", + $year+1900, $mon+1, $mday, $hour, $min, $sec; + $ret .= "
\n"; + return $ret; + }, + + 'postdemo' => + sub { + my($params) = shift; + my($ret) = "Ok\nresponse Not a highscore\n"; + my($db) = &allocdb("highscores.dbm", LOCK_EX); + my($key) = &getuniquekey($db); + my($candidate) = {}; + $candidate->{key} = $key; + $candidate->{score} = $params->{score}; + my($i); + for($i=0; $i<$MAXDEMOS; $i++) { + my($highkey) = sprintf "high%03i", $i; + if(!exists $db->{$highkey}) { + $ret = "Ok\nresponse Demo added\n"; + $ret .= "position $i\n"; + $ret .= "key $key\n"; + $db->{$key} = Dumper($params);; + $db->{$highkey} = Dumper($candidate); + last; + } + else { + my($high) = eval($db->{$highkey}); + if($candidate->{score} > $high->{score}) { + my($j); + for($j=$MAXDEMOS-1; $j>$i; $j--) { + my($key1) = sprintf "high%03i", $j; + my($key2) = sprintf "high%03i", $j-1; + if(exists $db->{$key2}) { + $db->{$key1} = $db->{$key2}; + } + } + $ret = "Ok\nresponse Demo added\n"; + $ret .= "position $i\n"; + $ret .= "key $key\n"; + $db->{$key} = Dumper($params);; + $db->{$highkey} = Dumper($candidate); + last; + } + if($candidate->{score} == $high->{score}) { + my($highdetails) = eval($db->{$high->{key}}); + if($params->{rec} eq $highdetails->{rec}) { + $ret = "Ok\nresponse Demo already here\n"; + $ret .= "position $i\n"; + $ret .= "key $high->{key}\n"; + last; + } + } + } + } + $ret .= &format(&getscores($db, $params->{num})); + &freedb($db, 1); + return $ret; + }, + + 'rebuildhighscores' => + sub { + my($params) = shift; + my($ret); + if(&superuser($params)) { + $ret = "Ok\n"; + my($db) = &allocdb("highscores.dbm", LOCK_EX); + my(@reaplist); + my(@highkeys); + my(@highscores); + my(@highnames); + my(%name_to_score); + while(($key, $val) = each(%$db)) { + if($key =~ 'high') { + push @reaplist, $key; + next; + } + my($debugging) = 0; + my($i); + my($data) = eval $val; + if(!defined $data->{name} || !defined $data->{score}) { + push @reaplist, $key; + next; + } + if($data->{score} < 150000) { + push @reaplist, $key; + next; + } + if($data->{name} eq "Hi Normy!!!") { + push @reaplist, $key; + next; + } + if($data->{name} eq "Norm") { + $debugging = 1; + } + my($inserted)=0; + if(defined $name_to_score{$data->{name}}) { + if($name_to_score{$data->{name}} > $data->{score}) { + next; + } + } + $name_to_score{$data->{name}} = $data->{score}; + for($i=0; $i<(scalar @highkeys); $i++) { + if($data->{score} > $highscores[$i] || ($data->{score} == $highscores[$i] && $key < $highkeys[$i])) { + my($j) = $i; + while($j < (scalar @highkeys)) { + if($highnames[$j] eq $data->{name}) { + last; + } + $j++; + } + for(; $j>$i; $j--) { + $highkeys[$j] = $highkeys[$j-1]; + $highscores[$j] = $highscores[$j-1]; + $highnames[$j] = $highnames[$j-1]; + } + $highkeys[$i] = $key; + $highscores[$i] = $data->{score}; + $highnames[$i] = $data->{name}; + $inserted=1; + if($debugging) { + $ret .= "debug inserted $data->{score} at $i\n"; + } + last; + } + } + if(!$inserted) { + if($debugging) { + $ret .= "debug appending $data->{score}\n"; + } + push @highkeys, $key; + push @highscores, $data->{score}; + push @highnames, $data->{name}; + } + } + $ret .= 'debug scalar @highkeys=='; + $ret .= scalar @highkeys; + $ret .= "\n"; + foreach $key (@reaplist) { + delete $db->{$key}; + } + my($i); + for($i=0; $i<$MAXDEMOS; $i++) { + my($highkey) = sprintf "high%03i", $i; + my($data) = eval $db->{$highkeys[$i]}; + my($infos) = {}; + $infos->{name} = $data->{name}; + $infos->{score} = $data->{score}; + $infos->{lines} = $data->{lines}; + $infos->{level} = $data->{level}; + $infos->{key} = $highkeys[$i]; + $db->{$highkey} = Dumper($infos); + } + + &freedb($db, 1); + } + else { + $ret = "Permission denied\n"; + } + return $ret; + }, + + 'deletedemo' => + sub { + my($params) = shift; + my($ret); + if(&superuser($params)) { + my($db) = &allocdb("highscores.dbm", LOCK_EX); + $ret = "Deleded demos\n"; + my(@reaplist); + while(($key, $val) = each(%$db)) { + if($key =~ "high.*") { + next; + } + else { + my($demo) = eval($val); + if($demo->{score} == $params->{score}) { + push @reaplist, $key; + $ret .= &format($demo, $key); + } + } + } + foreach $key (@reaplist) { + delete $db->{$key}; + } + &freedb($db, 1); + } + else { + $ret = "Permission denied\n"; + } + return $ret; + }, +); + +sub getscores { + my($db) = shift; + my($num) = shift; + $num = $DEFAULTDEMOS unless defined $num; + if($num > $MAXDEMOS) { + $num = $MAXDEMOS; + } + else { + if($num < 0) { + $num = 0; + } + } + my($ret) = {}; + my($i) = 0; + for($i=0; $i<$num; $i++) { + my($key) = sprintf "high%03i", $i; + if(exists $db->{$key}) { + my($key2) = eval($db->{$key}); + my($data) = eval($db->{$key2->{key}}); + $data->{date} = $key2->{key}; + $ret->{$key} = $data; + } + else { + last; + } + } + return $ret; +} + +&main; + +sub main { + my($q) = new CGI; + my($data) = $q->param('data'); + $data = "" unless defined $data; + my(@req) = split("\n", $data); + + my($cmd) = shift(@req); + $cmd = "nothing" unless defined $cmd; + my($params) = parse(@req); + + #Ajoute des params + $params->{info}{remoteaddr} = $q->remote_addr(); + + if(exists $commands{$cmd}) { + $reply = &{$commands{$cmd}}($params); + $reply = "\n" unless defined $reply; + } + else { + $reply = "Hi, I'm the Quadra game server.\n" . + "You should use Quadra to talk to me :).\n"; + } + if($cmd =~ /html$/) { + print $q->header('text/html'); + } + else { + print $q->header('text/plain'); + } + print $reply; +} + +sub getuniquekey { + my($db) = shift; + my($ret) = time . '.00'; + while(exists $db->{$ret}) { + $ret += 0.01; + } + return $ret; +} + +sub allocdb { + my($fn) = shift; + my($lock) = shift; + my(%db); + my($db) = tie(%db, "DB_File", $fn, O_CREAT|O_RDWR, 0666, $DB_HASH); + my($fd) = $db->fd; + undef $db; + open(DB_FH, "+<&=$fd"); + if($lock) { + flock(DB_FH, $lock); + } + return \%db; +} + +sub freedb { + my($db) = shift; + my($dbobj) = tied(%$db); + my($unlock) = shift; + $dbobj->sync; + if($unlock) { + flock(DB_FH, LOCK_UN); + } + undef $dbobj; + untie %$db; + close(DB_FH); +} + +sub parse { + my($ret); + $ret = {}; + foreach(@_) { + my($path, $val) = split(/\s/, $_, 2); + my($curr) = $ret; + while($path) { + $path =~ /([^\s\/]+)\/?(.*)/; + my($rep) = $1; + $path = $2; + if($path eq '/') { + $path = ''; + } + if(!exists($curr->{$rep})) { + if($path ne '') { + $curr->{$rep} = {}; + $curr = $curr->{$rep}; + } + else { + $curr->{$rep} = $val; + } + } + else { + $curr = $curr->{$rep}; + } + } + } + return $ret; +} + +sub format { + my($params) = shift; + my($root) = shift; + my($ret); + my($key, $val); + while(($key, $val) = each(%$params)) { + if(ref($val) ne 'HASH') { + if($root) { + $ret .= "$root/$key $val\n"; + } + else { + $ret .= "$key $val\n"; + } + } + else { + if(defined $root) { + $ret .= &format($val, "$root/$key"); + } + else { + $ret .= &format($val, "$key"); + } + } + } + return $ret; +} diff --git a/skelton/.cvsignore b/skelton/.cvsignore new file mode 100644 index 0000000..e7b97c5 --- /dev/null +++ b/skelton/.cvsignore @@ -0,0 +1 @@ +.depends diff --git a/skelton/ChangeLog b/skelton/ChangeLog new file mode 100644 index 0000000..32088bf --- /dev/null +++ b/skelton/ChangeLog @@ -0,0 +1,46 @@ +2000-08-07 Pierre Phaneuf + + * Incorporating various changes for public release. + +1999-10-26 Pierre Phaneuf + + * svgalib/input_x11.cpp: Added bare support for input methods, just + enough so that dead keys will work. + +1999-10-21 Pierre Phaneuf + + * common/net.cpp: added a SO_REUSEADDR socket option to the TCP socket + in Linux so that you can restart the game server right away. + +1999-10-01 Pierre Phaneuf + + * Added a precise frame counter to the Xlib implementation. + + * Improved X dirty rectangle handling a bit to do less superfluous color + conversions. + +1999-07-19 Pierre Phaneuf + + * X11 keyboard handling is done, and all the drawing primitives + are there. + +1999-07-14 Pierre Phaneuf + + * X11 support is mostly in, missing some drawing primitives, + keyboard handling isn't there and sound seems to have some problems. + +1999-07-08 Pierre Phaneuf + + * The input and video subsystems are now semi-dynamic, with better + error handling and improved terminal restoration. + +1999-06-04 Pierre Phaneuf + + * Disabled the Ctrl-c key in svgalib/input.cpp. + + * Fixed a bug in the RegistryLinux::read() method, there was no + terminating NULL at the end of the returned string. + + * Moved alt_tab out of the #ifdef UGS_DIRECTX, Inter::draw_zone() + needed it. + diff --git a/skelton/Makefile b/skelton/Makefile new file mode 100644 index 0000000..0a04c98 --- /dev/null +++ b/skelton/Makefile @@ -0,0 +1,14 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +MAKEFILE_INCLUDED:=yes + +.PHONY: default all release + +default: all + +release: + @$(MAKE) RELEASE=yes + +include config/common.mk + diff --git a/skelton/README b/skelton/README new file mode 100644 index 0000000..6f448d6 --- /dev/null +++ b/skelton/README @@ -0,0 +1,15 @@ + +Universal Game Skelton +---------------------- + +IMPORTANT!!! + +As much as there is "universal" in the name of this library and all +the original well-meaning of its authors, this is so terminally +hacked-up that you would probably go mad trying to use this in a new +project. One obvious example is that Xlib support is in "svgalib". + +DUH, OF COURSE! :-) + +You are much better off using something like SDL or Clanlib. + diff --git a/skelton/common/bitmap.cpp b/skelton/common/bitmap.cpp new file mode 100644 index 0000000..379152a --- /dev/null +++ b/skelton/common/bitmap.cpp @@ -0,0 +1,268 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "types.h" +#include "raw.h" +#include "pcx.h" +#include "video.h" +#include "res.h" +#include "bitmap.h" + +Bitmap* Bitmap::loadPcx(const char* n) { + Res_doze res(n); + Pcx pcx(res); + return new Bitmap(pcx); +} + +Bitmap* Bitmap::loadRaw(const char* n) { + Res_doze res(n); + Raw raw(res); + return new Bitmap(raw); +} + +Bitmap::Bitmap(int w, int h, int rw): + Clipable(w, h), + realwidth(rw*sizeof(T)), + zlines(new int[height]), + lines(new T*[height]), + size(h*rw), + fmem(1) { + directx = false; + initlines(); + setmem((void*)new Byte[size]); + clear(0); +} + +Bitmap::Bitmap(void* m, int w, int h, int rw): + Clipable(w, h), + realwidth(rw), + zlines(new int[height]), + lines(new T*[height]), + size(h*rw), + fmem(0) { + directx = false; + initlines(); + setmem(m); +} + +Bitmap::Bitmap(void* m, int w, int h, int rw, int bob): + Clipable(w, h), + realwidth(rw), + zlines(new int[height]), + lines(new T*[height]), + size(h*rw), + fmem(1) { + directx = false; + initlines(); + setmem((void*)new Byte[size]); + memcpy(mem, m, size); +} + +Bitmap::Bitmap(const Image& raw, bool dx): + Clipable(raw.width(), raw.height()), + realwidth(width*sizeof(T)), + zlines(new int[height]), + lines(new T*[height]), + size(height*realwidth), + fmem(1) { + directx = false; + initlines(); + setmem((void*)new Byte[size]); + reload(raw); + if(dx) + create_directx_surface(); +} + +void Bitmap::create_directx_surface() { +/*#ifdef UGS_DIRECTX + directx = true; + DDSURFACEDESC ddsd; + + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + + if(video->lpdd->CreateSurface(&ddsd, &directx_surface, NULL) != DD_OK) { + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + calldx(video->lpdd->CreateSurface(&ddsd, &directx_surface, NULL)); + } + + DDCOLORKEY color; + color.dwColorSpaceLowValue = 0; + color.dwColorSpaceHighValue = 0; + calldx(directx_surface->SetColorKey(DDCKEY_SRCBLT, &color)); + + video->add_surface(directx_surface, this); + copy_surface(); +#endif*/ +} + +void Bitmap::copy_surface() { +/*#ifdef UGS_DIRECTX + DDSURFACEDESC lock; + lock.dwSize=sizeof(lock); + lock.dwFlags=DDSD_ALL; + calldx(directx_surface->Lock(NULL, &lock, DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR, NULL)); + int pitch = lock.lPitch; + for(int i=0; iUnlock(NULL)); +#endif*/ +} + +void Bitmap::reload(const Image& raw) { + memcpy(mem, raw.pic(), size); +} + +void Bitmap::initlines() { + for(int y(0); yremove_surface(directx_surface, this); +#endif*/ + delete[] zlines; + delete[] lines; + if(fmem) + delete[] mem; +} + +void Bitmap::clear(const T color) const { + memset(mem, color, size); +} + +void Bitmap::draw(const Bitmap& d, const int dx, const int dy) const { + if(d.clip(dx, dy, this)) + return; + for(int y=clip_y1; y<=clip_y2; y++) { + memcpy(d[y]+clip_x1, operator[](y-dy)+(clip_x1-dx), clip_w); + } +} + +void Bitmap::draw(const Video_bitmap* d, const int dx, const int dy) const { + d->put_bitmap(*this, dx, dy); +} + +void Bitmap::hline(const int y, const int x, const int w, const Byte color) const { + if(clip(x, y, w, 1)) + return; + memset(operator[](y)+clip_x1, color, clip_w); +} + +void Bitmap::vline(const int x, const int y, const int h, const T color) const { + if(clip(x, y, 1, h)) + return; + for(int i=clip_y1; i<=clip_y2; i++) + operator[](i)[x]=color; +} + +void Bitmap::line(const int x1, const int y1, const int x2, const int y2, + const Byte color) const +{ + /* This function use the Bresenham's line algorithm to draw a line. */ + + int i, deltax, deltay, numpixels; + int d, dinc1, dinc2; + int x, xinc1, xinc2; + int y, yinc1, yinc2; + + // Calculate deltax and deltay for initialisation. + deltax = abs(x2 - x1); + deltay = abs(y2 - y1); + + // Special cases: vertical and horizontal lines. + if(y1 == y2) { + hline(y1, min(x1, x2), deltax, color); + return; + } + if(x1 == x2) { + vline(x1, min(y1, y2), deltay, color); + return; + } + + // Initialize all vars based on which is the independent variable. + if (deltax >= deltay) + { + // x is independent variable. + numpixels = deltax + 1; + d = (2 * deltay) - deltax; + dinc1 = deltay << 1; + dinc2 = (deltay - deltax) << 1; + xinc1 = 1; + xinc2 = 1; + yinc1 = 0; + yinc2 = 1; + } + else + { + // y is independent variable. + numpixels = deltay + 1; + d = (2 * deltax) - deltay; + dinc1 = deltax << 1; + dinc2 = (deltax - deltay) << 1; + xinc1 = 0; + xinc2 = 1; + yinc1 = 1; + yinc2 = 1; + } + + // Make sure x and y move in the right directions. + if (x1 > x2) + { + xinc1 = -xinc1; + xinc2 = -xinc2; + } + if (y1 > y2) + { + yinc1 = -yinc1; + yinc2 = -yinc2; + } + + // Start drawing at . + x = x1; + y = y1; + + // Draw the pixels. + for (i = 1; i <= numpixels; i++) + { + // Draw pixel. + if(clip(x, y, 1, 1)) + return; + operator[](y)[x] = color; + + // Calculate next step. + if (d < 0) + { + d = d + dinc1; + x = x + xinc1; + y = y + yinc1; + } + else + { + d = d + dinc2; + x = x + xinc2; + y = y + yinc2; + } + } +} + +void Bitmap::put_pel(const int x, const int y, const Byte color) const { + if(clip(x, y, 1, 1)) + return; + fast_pel(x, y, color); +} diff --git a/skelton/common/buf.cpp b/skelton/common/buf.cpp new file mode 100644 index 0000000..96bd735 --- /dev/null +++ b/skelton/common/buf.cpp @@ -0,0 +1,122 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include +#include "error.h" +#include "buf.h" + +Buf::Buf(Dword size, Dword in) { + data=NULL; + size_=0; + capacity=0; + inc=in; + resize(size); +} + +Buf::~Buf() { + if(data) + free(data); +} + +void Buf::remove_from_start(Dword s) { + Dword current_size=size(); + if(ssize()) { + Dword end = size(); + Dword endsize = s-size(); + reserve(s); + memset(get()+end, 0, endsize); + } + size_=s; +} + +void Buf::reserve(Dword s) { + //Round up to next inc + s=((s+inc-1)/inc)*inc; + if(s>capacity) { + data = (Byte*)realloc(data, s); + if(!data) + (void)new Error("Out of memory!"); + capacity=s; + } +} + +Textbuf::Textbuf(Dword size) { + data=NULL; + capacity=0; + reserve(size); +} + +Textbuf::~Textbuf() { + if(data) + free(data); +} + +int Textbuf::len() const { + if(data) + return strlen(data); + else + return 0; +} + +char* Textbuf::get() const { + static char st=0; + if(data) + return data; + else + return &st; +} + +void Textbuf::append(const char* s, ...) { + char st[32768]; + va_list marker; + va_start(marker, s); + vsprintf(st, s, marker); + va_end(marker); + if(data) { + reserve(strlen(data)+strlen(st)+1); + } + else { + reserve(strlen(st)+1); + } + if(data) + strcat(data, st); +} + +void Textbuf::reserve(Dword size) { + Dword wanted=(size+15)/16*16; + if(wanted<=capacity) + return; + bool init=data? false:true; + data=(char*)realloc(data, wanted); + if(!data) + (void)new Error("Out of memory!"); + capacity=wanted; + if(init) + data[0]=0; +} diff --git a/skelton/common/clipable.cpp b/skelton/common/clipable.cpp new file mode 100644 index 0000000..30d4300 --- /dev/null +++ b/skelton/common/clipable.cpp @@ -0,0 +1,40 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "array.h" +#include "bitmap.h" +#include "clipable.h" + +int Clipable::clip_x1; +int Clipable::clip_x2; +int Clipable::clip_y1; +int Clipable::clip_y2; +int Clipable::clip_w; + +bool Clipable::clip(const int x, const int y, const int w, const int h) const { + clip_x1=max(0, x); + if(clip_x1>=width) + return true; + clip_x2=min(width-1, x+w-1); + if(clip_x2<0) + return true; + clip_y1=max(0, y); + if(clip_y1>=height) + return true; + clip_y2=min(height-1, y+h-1); + if(clip_y2<0) + return true; + clip_w = clip_x2-clip_x1+1; + return false; +} + +bool Clipable::clip(const int x, const int y, const Bitmap &b) const { + return clip(x, y, b.width, b.height); +} + +bool Clipable::clip(const int x, const int y, const Bitmap *b) const { + return clip(x, y, b->width, b->height); +} diff --git a/skelton/common/clock.cpp b/skelton/common/clock.cpp new file mode 100644 index 0000000..0408fc4 --- /dev/null +++ b/skelton/common/clock.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include "clock.h" + +Clock::Clock() { +} + +int Clock::get_time() { + time_t t; + time(&t); + return (int) t; +} + +char *Clock::time2char(int time) { + static char st[20]; + const time_t ti=time; + tm *t = localtime(&ti); + if(t) { + sprintf(st, "%04i-%02i-%02i %2i:%02i:%02i", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + } + else { + st[0]=0; + } + return st; +} + +char *Clock::absolute_time() { + static char st[64]; +#ifdef UGS_LINUX + struct timeb time_info; + ftime(&time_info); + int tz=time_info.timezone; +#endif +#ifdef UGS_DIRECTX + struct _timeb time_info; + _ftime(&time_info); + int tz=_timezone; +#endif + const time_t ti=get_time(); + tm *t = localtime(&ti); + if(t) { + sprintf(st, "%04i.%02i.%02i.%2i.%02i.%02i.%03i.%+2.1f", + t->tm_year+1900, + t->tm_mon+1, + t->tm_mday, + t->tm_hour, + t->tm_min, + t->tm_sec, + time_info.millitm, + -tz/3600.0 + ); + } + else { + st[0]=0; + } + return st; +} diff --git a/skelton/common/command.cpp b/skelton/common/command.cpp new file mode 100644 index 0000000..306e9f0 --- /dev/null +++ b/skelton/common/command.cpp @@ -0,0 +1,79 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include "error.h" +#include "command.h" + +Command::Command() { + st[0] = 0; +} + +Command::~Command() { +} + +void Command::add(const char *s) { + char* i; + i = st + strlen(st); + while(*s && i=param && (*temp==' ' || *temp=='\t')) //Remove trailing spaces/tabs + *temp-- = 0; + if(strlen(param)) + return param; + else + return NULL; // si pas de param +} + +Command command; diff --git a/skelton/common/crypt.cpp b/skelton/common/crypt.cpp new file mode 100644 index 0000000..6a7f007 --- /dev/null +++ b/skelton/common/crypt.cpp @@ -0,0 +1,365 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include +#include +#include +#include "types.h" +#include "crypt.h" + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 [4], unsigned char [64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); +static void MD5_memcpy(POINTER, POINTER, unsigned int); +static void MD5_memset(POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +Crypt::Crypt() { + //Init a "running" crypter + finalized=false; + MD5Init(&context); +} + +Crypt::Crypt(const char *s, bool do_shuffle) { + //Init a "instant" crypter + finalized=true; + MD5Init(&context); + step((Byte *)s, strlen(s)); + finalize(do_shuffle); +} + +Crypt::~Crypt() { + //Clear sensitive stuff + memset(digest, 0, sizeof(digest)); + memset(result, 0, sizeof(result)); +} + +void Crypt::step(Byte *buf, Dword size) { + MD5Update(&context, buf, size); +} + +void Crypt::step2(Byte *buf, Dword size) { + MD5Update(&context, buf, size); +} + +void Crypt::finalize(bool do_shuffle) { + MD5Final(digest, &context); + if(do_shuffle) + shuffle(); + char tube[16]; + result[0] = 0; + for(int i = 0; i < 16; i++) { + sprintf(tube, "%02x", digest[i]); + strcat(result, tube); + } + finalized=true; +} + +const char *Crypt::get_digest_string() const { + return result; +} + +const Byte *Crypt::get_digest() const { + return digest; +} + +void Crypt::shuffle() { + for(int i = 0; i < 16; i++) { + digest[i] ^= 255; + digest[i] += 53 + i * 7; + } +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void Crypt::MD5Init(MD5_CTX *context) { + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. */ +void Crypt::MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen) { + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if(inputLen >= partLen) { + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for(i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); +} + +void Crypt::MD5Update2(MD5_CTX *context, unsigned char *input, unsigned int inputLen) { + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if(inputLen >= partLen) { + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for(i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. */ +void Crypt::MD5Final(unsigned char digest[16], MD5_CTX *context) { + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + MD5_memset((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ +static void MD5Transform(UINT4 state[4], unsigned char block[64]) { + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + MD5_memset((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. */ +static void Encode(unsigned char *output, UINT4 *input, unsigned int len) { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. */ +static void Decode(UINT4 *output, unsigned char *input, unsigned int len) { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +static void MD5_memcpy(POINTER output, POINTER input, unsigned int len) { + memcpy(output, input, len); +} + +static void MD5_memset(POINTER output, int value, unsigned int len) { + memset(output, value, len); +} diff --git a/skelton/common/crypt.pl b/skelton/common/crypt.pl new file mode 100755 index 0000000..35271d7 --- /dev/null +++ b/skelton/common/crypt.pl @@ -0,0 +1,18 @@ +#! /usr/bin/perl -Tw + +use strict; +use MD5; + +my($input, $hash, $i, $item); + +chomp($input = ); + +$hash = MD5->hash($input); + +for($i = 0; $i < 16; $i++) { + $item = unpack("C", substr($hash, $i, 1)); + $item = (($item ^ 255) + (53 + $i * 7)) % 256; + substr($hash, $i, 1) = pack("C", $item); +} + +print unpack("H*", $hash); diff --git a/skelton/common/cursor.cpp b/skelton/common/cursor.cpp new file mode 100644 index 0000000..f2aea4e --- /dev/null +++ b/skelton/common/cursor.cpp @@ -0,0 +1,22 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "video.h" +#include "cursor_self.h" +#ifdef UGS_LINUX_X11 +#include "cursor_x11.h" +#endif +#include "cursor.h" + +Cursor* cursor = NULL; + +Cursor* Cursor::New(Sprite* s) { +#ifdef UGS_LINUX_X11 + if(video->xwindow) + return new Cursor_X11(); +#endif + return new Cursor_Self(s); +} diff --git a/skelton/common/cursor_self.cpp b/skelton/common/cursor_self.cpp new file mode 100644 index 0000000..7a644d6 --- /dev/null +++ b/skelton/common/cursor_self.cpp @@ -0,0 +1,124 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "input.h" +#include "video.h" +#include "cursor_self.h" + +Cursor_Self::Cursor_Self(Sprite* s) { + change_cursor(s); + set_pos(video->width/2, video->height/2); + set_limit(0, 0, video->width, video->height); + back[0].dirty = false; + back[0].buf = NULL; + back[1].dirty = false; + back[1].buf = NULL; + page = 0; + set_speed(64); + restore_back(true); + mouse_rate=0; + smooth_mode=true; // enable le smooth par defaut + visible = true; +} + +Cursor_Self::~Cursor_Self() { + if(back[0].buf) + delete back[0].buf; + if(back[1].buf) + delete back[1].buf; +} + +void Cursor_Self::change_cursor(Sprite* s) { + spr = s; +} + +void Cursor_Self::restore_back(bool r) { + _restore_back = r; +} + +void Cursor_Self::set_pos(int px, int py) { + x = sx = px; + y = sy = py; +} + +void Cursor_Self::set_limit(int x1, int y1, int x2, int y2) { + lx1 = x1; + ly1 = y1; + lx2 = x2-1; + ly2 = y2-1; +} + +void Cursor_Self::set_speed(const Byte s) { + speed = max(1, s); + pool_x = pool_y = 0; +} + +void Cursor_Self::move() { + int nx, ny; // nouvelle pos x,y + pool_x += input->mouse.dx * speed; + pool_y += input->mouse.dy * speed; + int temp_x, temp_y; // valeur ajuste de pool_x et pool_y + temp_x = (pool_x) >> 6; + temp_y = (pool_y) >> 6; + pool_x -= (temp_x << 6); + pool_y -= (temp_y << 6); + nx = sx+temp_x; + ny = sy+temp_y; + if(nx > lx2) + nx = lx2; + if(nx < lx1) + nx = lx1; + if(ny > ly2) + ny = ly2; + if(ny < ly1) + ny = ly1; + if(smooth_mode) { // mode smooth: interpolation des mouvements (pour serial mouse a 35 update/seconde) + if(sx != nx || sy != ny) { + x = (sx+nx) >> 1; + sx = nx; + y = (sy+ny) >> 1; + sy = ny; + mouse_rate++; + if(mouse_rate > 45) // si la souris s'update plus que 40 frame/seconde, + smooth_mode = false; // desactive le smoothing + } else { + x = nx; + y = ny; + mouse_rate=0; + } + } else { // mode normal: pour bus mouse rapide + x = sx = nx; + y = sy = ny; + } +} + +void Cursor_Self::get_back() { + if(_restore_back && spr && visible) { + back[page].x = x-spr->hot_x; + back[page].y = y-spr->hot_y; + back[page].dirty = true; + if(back[page].buf && back[page].buf->width != spr->width) { + delete back[page].buf; + back[page].buf = NULL; + } + if(back[page].buf == NULL) + back[page].buf = new Bitmap(spr->width, spr->height, spr->width); + video->vb->get_bitmap(back[page].buf, back[page].x, back[page].y, spr->width, spr->height); + } + page = 1-page; +} + +void Cursor_Self::put_back() { + if(back[page].dirty) { + back[page].buf->draw(video->vb, back[page].x, back[page].y); + back[page].dirty = false; + } +} + +void Cursor_Self::draw() const { + if(spr && visible) + spr->draw(video->vb, x, y); +} diff --git a/skelton/common/debug.cpp b/skelton/common/debug.cpp new file mode 100644 index 0000000..6f0db6c --- /dev/null +++ b/skelton/common/debug.cpp @@ -0,0 +1,45 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "debug.h" + +#ifdef _DEBUG + +AllocNode* Debug::allocs = NULL; + +bool Debug::initialized = false; + +void Debug::dumpAllocs() { + AllocNode* ptr; + AllocNode* next; + + if(allocs) { + skelton_msgbox("*** MEMORY LEAKS ***\n"); + + ptr = allocs; + + while(ptr) { + next = ptr->next; + + if(ptr->funcName) + skelton_msgbox("%s:%i: %s (%i bytes) in \"%s\"\n", + ptr->fileName, ptr->lineNo, ptr->className, ptr->size, ptr->funcName); + else + skelton_msgbox("%s:%i: %s (%i bytes)\n", + ptr->fileName, ptr->lineNo, ptr->className, ptr->size); + + free(ptr->fileName); + free(ptr->funcName); + free(ptr->className); + + delete ptr; + ptr = next; + } + } +} + +#endif + diff --git a/skelton/common/dict.cpp b/skelton/common/dict.cpp new file mode 100644 index 0000000..5b219b7 --- /dev/null +++ b/skelton/common/dict.cpp @@ -0,0 +1,97 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include "error.h" +#include "dict.h" + +Dict::Dict(const char *k, const char *v) { + if(k) { + strncpy(key, k, sizeof(key)-1); + key[sizeof(key)-1] = 0; + } + else + key[0] = 0; + + if(v) + value.append("%s", v); +} + +Dict::~Dict() { + sub.deleteall(); +} + +void Dict::add(const char *s) { + Textbuf buf; + buf.append("%s", s); + char *st=buf.get(); + char *val = strchr(st, ' '); + char *rep = strchr(st, '/'); + if(rep && val && rep>val) + rep=NULL; + + if(!rep) { + if(val) { + *val = 0; + sub.add(new Dict(st, val+1)); + } else { + sub.add(new Dict(st)); + } + } else { + *rep = 0; + + Dict *d = find_sub(st); + if(!d) { + d = new Dict(st); + sub.add(d); + } + d->add(rep+1); + } +} + +void Dict::dump() const { + if(sub.size()) { + for(int i=0; idump(); + } + } else { + msgbox("key=[%s], value=[%-20.20s]\n", key, value.get()); + } +} + +Dword Dict::size() const { + return sub.size(); +} + +const char *Dict::get_key() const { + return key; +} + +const char *Dict::find(const char *s) const { + Dict *d = find_sub(s); + if(d) + return d->value.get(); + else + return NULL; +} + +Dict *Dict::find_sub(const char *s) const { + for(int i=0; ikey, s) == 0) { + return sub[i]; + } + } + return NULL; +} + +Dict *Dict::get_sub(const int i) const { + if(i +#include +#include +#include +#include "error.h" +#include "http_post.h" + +Http_post::Http_post(const char *host, int port, const char *path): Http_request(host, port), data(0, 1024) { + init(path); +} + +Http_post::Http_post(Dword hostaddr, int port, const char *path): Http_request(hostaddr, port), data(0, 1024) { + init(path); +} + +Http_post::~Http_post() { +} + +void Http_post::init(const char *path) { + strcpy(cgi, path); +} + +void Http_post::add_data_encode(const char* m, ...) { + char st[32768]; + Textbuf buf; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + Http_request::url_encode(st, buf); + add_data_raw(buf.get()); +} + +void Http_post::add_data_raw(const char* m) { + data.append((const Byte*)m, strlen(m)); +} + +void Http_post::send() { + url.resize(0); + url.append("POST "); + url.append(cgi); + url.append(" HTTP/1.0\r\nContent-type: application/x-www-form-urlencoded\r\nContent-length: "); + char st[16]; + sprintf(st, "%i\r\n\r\n", data.size()); + url.append(st); + url.append(data.get(), data.size()); + request = (Byte*)url.get(); + size = url.size(); + /* + st[0]=0; + url.append((Byte*)st, 1); //So the following msgbox won't crash + msgbox("Http_post::send: data: \n{\n%s\n} size=%i\n", request, size); + */ +} diff --git a/skelton/common/http_request.cpp b/skelton/common/http_request.cpp new file mode 100644 index 0000000..e21a369 --- /dev/null +++ b/skelton/common/http_request.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "error.h" +#include "net.h" +#include "http_request.h" + +char Http_request::base64table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', +}; + +Byte Http_request::reversebase64table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, + 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void Http_request::base64encode(const Byte *in, Textbuf& out, Dword size) { + char* table = base64table; + Byte a, b, c; + Dword i; + char o[5]; + o[4]=0; + for(i=0; i<(size+2)/3; i++) { + a = i*3+0>2]; + o[1] = table[((a&3)<<4) | (b>>4)]; + o[2] = table[((b&15)<<2) | (c>>6)]; + o[3] = table[c&63]; + out.append("%s", o); + } + if(size%3) { + char *end=out.get()+strlen(out.get())-1; + switch(size%3) { + case 1: + *end--='='; + //pas de break, c'est normal! + case 2: + *end--='='; + } + } +} + +void Http_request::base64decode(const char *in, Buf& out, Dword size) { + Byte* table = reversebase64table; + if(size&3) { + skelton_msgbox("Http_request::base64decode: incorrect size\n"); + return; + } + Byte a, b, c, d; + Dword i; + for(i=0; i>4); + o[1] = (table[b]<<4) | (table[c]>>2); + o[2] = (table[c]<<6) | (table[d]); + int n=3; + if(d=='=') + n--; + if(c=='=') + n--; + out.append(o, n); + } +} + +void Http_request::url_encode(const char *src, Textbuf& dest) { + char tmp[4]; + while(*src) { + tmp[0] = *src++; + tmp[1] = 0; + if(tmp[0] < 48 || tmp[0] > 122 || (tmp[0] >= 58 && tmp[0] <= 64)) + sprintf(tmp, "%c%02X", '%', (Byte)tmp[0]); // convertit en '%FF' url + dest.append("%s", tmp); + } +} + +Http_request::Http_request(const char *host, int port, const Byte *request, int size) { + if(request) { + this->request=request; + this->size=size? size:strlen((const char *)request); + } + nc=net->start_other(host, port); // nc peut etre NULL en cas d'erreur! + sent=false; +} + +Http_request::Http_request(Dword hostaddr, int port, const Byte *request, int size) { + if(request) { + this->request=request; + this->size=size? size:strlen((const char *)request); + } + nc=net->start_other(hostaddr, port); // nc peut etre NULL en cas d'erreur! + sent=false; +} + +Http_request::~Http_request() { + if(nc) + delete nc; +} + +void Http_request::sendrequest() { + if(nc) { + nc->sendtcp(request, size); + nc->commit(); + } + sent=true; +} + +Byte *Http_request::getbuf() const { + return buf.get(); +} + +Dword Http_request::getsize() const { + return buf.size()? buf.size()-1:0; //Don't count nul byte added in 'done' +} + +bool Http_request::isconnected() const { // indique si la connexion a ete etablie + if(nc && nc->state() == Net_connection::connected) + return true; + return false; +} + +bool Http_request::done() { + Net_connection::Netstate state; + if(nc) + state = nc->state(); + else + state = Net_connection::invalid; + if(state==Net_connection::invalid || state==Net_connection::disconnected) { + Byte st=0; + buf.append(&st, 1); + return true; + } + if(statereceivetcp(tmp, 4096); + if(tube) + buf.append(tmp, tube); + if(!tube && nc->state()==Net_connection::disconnected) { + Byte st=0; + buf.append(&st, 1); + return true; + } + return false; +} + +Dword Http_request::gethostaddr() const { + if(nc) + return nc->getdestaddr(); + else + return 0; +} + +int Http_request::gethostport() const { + if(nc) + return nc->getdestport(); + else + return 0; +} diff --git a/skelton/common/id.cpp b/skelton/common/id.cpp new file mode 100644 index 0000000..c384751 --- /dev/null +++ b/skelton/common/id.cpp @@ -0,0 +1,9 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "id.h" + +Dword Identifyable::next_id=1; diff --git a/skelton/common/input_dumb.cpp b/skelton/common/input_dumb.cpp new file mode 100644 index 0000000..ff77797 --- /dev/null +++ b/skelton/common/input_dumb.cpp @@ -0,0 +1,54 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "error.h" +#include "main.h" +#include "input_dumb.h" + +Input_Dumb::Input_Dumb() { + mouse.dx = mouse.dy = mouse.dz = 0; + mouse.quel = -1; + int i; + for(i=0; i<4; i++) + mouse.button[i] = 0; + + quel_key = -1; + pause = false; + for(i=0; i<256; i++) + keys[i] = 0; + clear_key(); +} + +Input_Dumb::~Input_Dumb() { +} + +void Input_Dumb::clear_key() { + process_key(); // vide la queue de touche + shift_key = 0; + quel_key = -1; + key_pending = 0; + for(int i=0; i<256; i++) + keys[i] = 0; +} + +void Input_Dumb::process_key() { +} + +void Input_Dumb::process_mouse() { + mouse.dx = mouse.dy = mouse.dz = 0; +} + +void Input_Dumb::add_key_buf(char c, bool special) { + if(key_pending < MAXKEY) { + key_buf[key_pending].c = c; + key_buf[key_pending].special = special; + key_pending++; + } +} + +void Input_Dumb::restore() { + clear_key(); +} diff --git a/skelton/common/inter.cpp b/skelton/common/inter.cpp new file mode 100644 index 0000000..6db83ea --- /dev/null +++ b/skelton/common/inter.cpp @@ -0,0 +1,1413 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "video.h" +#include "bitmap.h" +#include "input.h" +#include "main.h" +#include "sound.h" +#include "cursor.h" +#include "inter.h" +#include "res.h" + +int Inter::last_mouse_x = -1, Inter::last_mouse_y = -1; +bool Inter::kb_visible = false; + +Zone::Zone(Inter* in, int px, int py, int pw, int ph) { + dirt(); + focusable = 0; + kb_focusable = stay_on_top = false; + child = NULL; + parent = NULL; + x = px; + y = py; + w = pw; + h = ph; + enabled = 0; + inter = in; + if(inter) + inter->add(this); +} + +Zone::~Zone() { + if(inter) + inter->remove(this); +} + +void Zone::clicked(int quel) { + if(child) + child->clicked(quel); +} +void Zone::double_clicked() { + if(child) + child->double_clicked(); +} +void Zone::entered() { + if(child) + child->entered(); +} +void Zone::waiting() { + if(child) + child->waiting(); +} +void Zone::leaved() { + if(child) + child->leaved(); +} +void Zone::set_child(Zone* chil) { + child = chil; + child->parent = this; + child->kb_focusable = false; // empeche de focuser un 'child' +} + +int Zone::in() const { + if(!cursor) + return 0; + if(cursor->x >= x && cursor->x < (x+w) && + cursor->y >= y && cursor->y < (y+h)) + return 1; + else + return 0; +} + +Zone_sprite::Zone_sprite(Inter *in, const char *nam, int px, int py): Zone(in) { + Res_doze res(nam); + Raw raw(res); + Bitmap bitmap(raw); + sp = new Sprite(bitmap, 0, 0); + w = sp->width; + h = sp->height; + if(px == -1) + x = (video->width - w) / 2; + if(py == -1) + y = (video->height - h) / 2; + w = h = 0; // special: zone_sprite pas clickable + stay_on_top = true; // special: les zone_sprite se reblittent constamment. +} + +Zone_sprite::~Zone_sprite() { + delete sp; +} + +void Zone_sprite::draw() { + sp->draw(video->vb, x, y); +} + +Zone_bitmap::Zone_bitmap(Inter* in, Bitmap* bit, int px, int py, Bitmap* bit2): +Zone(in, px, py, bit->width, bit->height) { + bit_ = bit; + bit2_ = bit2; + actual = bit; + del_bit = false; +} + +Zone_bitmap::Zone_bitmap(Inter* in, Bitmap* bit, int px, int py, bool del): +Zone(in, px, py, bit->width, bit->height) { + bit_ = bit; + bit2_ = NULL; + actual = bit; + del_bit = del; +} + +Zone_bitmap::~Zone_bitmap() { + if(del_bit) + delete bit_; +} + +Zone_state::Zone_state(Inter* in, int *pval, int px, int py, int pw, int ph, int pnstate): +Zone_watch_int(in, pval, px, py, pw, ph) { + nstate = pnstate; + kb_focusable = true; +} + +void Zone_watch_int::set_val(int *pv) { + val = pv; + last_val = -9999; + process(); +} + +void Zone_watch_int::process() { + if(val && last_val != *val) { + dirt(); + last_val = *val; + } +} + +void Zone_state::clicked(int quel) { + if(quel==0) { + *val = (*val) + 1; + if(*val > (nstate-1)) + *val = 0; + } + else { + *val = (*val) - 1; + if(*val < 0) + *val = (nstate-1); + } + process(); + Zone_watch_int::clicked(quel); +} + +Zone_state_bit::Zone_state_bit(Inter* in, const char* b1, int *pval, int px, int py, const char* b2, const char* b3): + Zone_state(in, pval, px, py) { + Res_doze res(b1); + Raw raw(res); + state[0] = new Bitmap(raw); + w = state[0]->width; + h = state[0]->height; + if(b2) { + Res_doze res2(b2); + Raw raw(res2); + state[nstate++] = new Bitmap(raw); + } + if(b3) { + Res_doze res3(b3); + Raw raw(res3); + state[nstate++] = new Bitmap(raw); + } +} + +Zone_state_text::Zone_state_text(Inter* in, int *pval, int px, int py, int pw, int ph): + Zone_state(in, pval, px, py, pw, ph, 0) { + pan = new Zone_panel(NULL, x, y, w, h); +} + +void Zone_state_text::add_string(const char* s, Font *f) { + if(!f) + f = inter->font; + fonts[nstate] = f; + state[nstate++] = s; + int w2 = f->width(s) + 6; + if(w2 > w) { + w = w2; + pan->w = w; + dirt(); + pan->resize(); + } +} + +void Zone_state_text::draw() { + pan->draw(); + video->vb->vline(x, y, h, 255); + video->vb->hline(y, x, w, 255); + fonts[last_val]->draw(state[last_val], pan->pan, CENTER, 0); +} + +void Zone_state_text::leaved() { + Zone_state::leaved(); + pan->high = false; + pan->dirt(); + dirt(); +} + +void Zone_state_text::entered() { + Zone_state::entered(); + pan->high = true; + pan->dirt(); + dirt(); +} + +Zone_text::Zone_text(Inter* in, const char* s, int px, int py): +Zone(in, px, py, in->font->width(s), in->font->height()) { + font = in->font; + lock_size = false; + set_text(s); +} + +Zone_text::Zone_text(Font* f2, Inter* in, const char* s, int px, int py): +Zone(in, px, py, in->font->width(s)+2, f2->height()) { + font = f2; + lock_size = false; + set_text(s); +} + +Zone_text::Zone_text(Font* f2, Inter* in, const char* s, int py): +Zone(in, 0, py, video->width, f2->height()) { + font = f2; + lock_size = true; + set_text(s); +} + +Zone_text::Zone_text(Inter* in, const char* s, int px, int py, int pw): +Zone(in, px, py, pw, in->font->height()) { + font = in->font; + lock_size = true; + set_text(s); +} + +Zone_text::Zone_text(Inter* in, const char* s, int py): +Zone(in, 0, py, video->width, in->font->height()) { + font = in->font; + lock_size = true; + set_text(s); +} + +void Zone_text::set_font(Font* f) { + font = f; + dirt(); +} + +void Zone_text::set_text(const char* s) { + strncpy(st, s, sizeof(st)); + st[sizeof(st)-1]=0; + if(!lock_size) { + w = font->width(s); + h = font->height(); + text_x = x; + } else + text_x = (w-font->width(s))/2 + x; + dirt(); +} + +void Zone_text::draw() { + font->draw(st, video->vb, text_x, y); +} + +Zone_text_select::Zone_text_select(Inter* in, Font* f, const char* s, int px, int py): +Zone_text(in, s, px, py) { + font2 = f; + actual = in->font; + kb_focusable = true; +} + +Zone_text_select::Zone_text_select(Inter* in, Font* f, const char* s, int px, int py, int pw): +Zone_text(in, s, px, py, pw) { + font2 = f; + actual = in->font; + kb_focusable = true; +} + +void Zone_text_select::draw() { + video->vb->hline(y, x, w, 210); + video->vb->hline(y+h-1, x, w, 210); + video->vb->vline(x, y, h, 210); + video->vb->vline(x+w-1, y, h, 210); + actual->draw(st, video->vb, text_x, y); +} + +void Zone_text_select::leaved() { + actual = font; + dirt(); + Zone_text::leaved(); +} + +void Zone_text_select::entered() { + actual = font2; + dirt(); + Zone_text::entered(); +} + +void Zone_text_select::set_font(Font* f) { + if(actual == font) + actual = f; + Zone_text::set_font(f); +} + +Zone_text_button::Zone_text_button(Inter* in, Bitmap *fond, Font* f, const char* s, int py): +Zone_text_select(in, f, s, (video->width - f->width(s))>>1, py-2, f->width(s)) { + h+=4; + w+=6; + set_bit(fond); + high = false; + kb_focusable = true; +} + +Zone_text_button::Zone_text_button(Inter* in, Bitmap *fond, Font* f, const char* s, int px, int py): +Zone_text_select(in, f, s, px-3, py-2) { + h+=4; + w+=6; + set_bit(fond); + high = false; + kb_focusable = true; +} + +Zone_text_button::Zone_text_button(Inter* in, Bitmap *fond, Font* f, const char* s, int px, int py, int pw): +Zone_text_select(in, f, s, px-3, py-2, pw+6) { + h+=4; + set_bit(fond); + high = false; + kb_focusable = true; +} + +Zone_text_button::~Zone_text_button() { + if(bit) + delete bit; +} + +void Zone_text_button::set_text(const char* s) { + Zone_text_select::set_text(s); + if(!lock_size) { + h+=4; + w+=6; + } +} + +void Zone_text_button::set_bit(Bitmap *fond) { + if(fond) + bit = new Bitmap((*fond)[y]+x, w-2, h-2, fond->realwidth); + else + bit = NULL; +} + +void Zone_text_button::draw() { + if(!bit) + video->vb->rect(x+1, y+1, w-2, h-2, 0); + if(high) { + if(bit) + bit->draw(video->vb, x+2, y+2); + actual->draw(st, video->vb, text_x+4, y+3); + video->vb->vline(x, y, h, 0); + video->vb->hline(y, x, w, 0); + video->vb->vline(x+w-1, y+1, h-1, 0); + video->vb->hline(y+h-1, x+1, w-1, 0); + video->vb->vline(x+1, y+1, h-2, 0); + video->vb->hline(y+1, x+1, w-2, 0); + video->vb->vline(x+w-2, y+2, h-3, 255); + video->vb->hline(y+h-2, x+2, w-3, 255); + } else { + if(bit) + bit->draw(video->vb, x, y); + actual->draw(st, video->vb, text_x+3, y+2); + video->vb->vline(x, y, h, 0); + video->vb->hline(y, x, w, 0); + video->vb->vline(x+w-1, y+1, h-1, 0); + video->vb->hline(y+h-1, x+1, w-1, 0); + video->vb->vline(x+1, y+1, h-2, 255); + video->vb->hline(y+1, x+1, w-2, 255); + video->vb->vline(x+w-2, y+2, h-3, 0); + video->vb->hline(y+h-2, x+2, w-3, 0); + } +} + +void Zone_text_button::entered() { + Zone_text_select::entered(); + high = true; +} + +void Zone_text_button::leaved() { + Zone_text_select::leaved(); + high = false; +} + +Zone_panel::Zone_panel(Inter* in, int px, int py, int pw, int ph): +Zone(in, px, py, pw, ph) { + draw_frame = true; + pan = NULL; + high = false; + resize(); +} + +void Zone_panel::resize() { + if(pan) + delete pan; + pan = Video_bitmap::New(x+2, y+2, max(w-4, 0), max(h-4, 0)); + dirt(); +} + +void Zone_panel::draw() { + if(draw_frame) { + video->vb->hline(y, x, w, 210); + video->vb->hline(y+h-1, x, w, 210); + video->vb->vline(x, y+1, h-2, 210); + video->vb->vline(x+w-1, y+1, h-2, 210); + if(high) { + video->vb->hline(y+1, x+1, w-2, 255); + video->vb->hline(y+h-2, x+1, w-2, 255); + video->vb->vline(x+1, y+2, h-4, 255); + video->vb->vline(x+w-2, y+2, h-4, 255); + if(h-4 > 0) + video->vb->rect(x+2, y+2, w-4, h-4, 0); + } else { + if(h-2 > 0) + video->vb->rect(x+1, y+1, w-2, h-2, 0); + } + } + pan->setmem(); +} + +Zone_text_input::Zone_text_input(Inter* in, const Palette& pal, char* s, int mlen, int px, int py, int pw, int mwidth): +Zone_panel(in, px, py, pw, in->font->height()+2) { + set_val(s); + focusable = 1; + focus = 0; + maxlen = mlen-1; + maxwidth = mwidth; + kb_focusable = true; + + Remap temp(pal); + temp.findrgb(0, 255, 255, 0); + curcolor = temp.map[0]; + + font_selected = new Font(in->font->fdata_original, pal, 0, 0, 0, 255, 255, 0); + select_start = -1; + panx = 0; +} + +Zone_text_input::~Zone_text_input() { + if(focus) + lost_focus(1); // force une perte de focus si delete zone + delete font_selected; +} + +void Zone_text_input::set_val(char* s) { + if(s) + strcpy(st, s); + else + st[0] = 0; + val = s; + dirt(); +} + +void Zone_text_input::clicked(int quel) { + if(focus == 0) { + focus = 10; + curpos = strlen(st); + actual_len = curpos; + select_start = 0; // select_all par defaut + panx = 0; + input->deraw(); + input->clear_key(); + Zone_panel::clicked(quel); + high = true; + dirt(); + first_click = false; + } else { + first_click = true; + if(input->shift_key & SHIFT) { + if(select_start == -1) + select_start = curpos; + } else { + select_start = -1; + } + set_mouse_curpos(); + dirt(); + } +} + +void Zone_text_input::waiting() { + if(focus != 0 && first_click) { + if(input->mouse.button[0] & PRESSED) { + if(select_start == -1) + select_start = curpos; + set_mouse_curpos(); + } + } +} + +void Zone_text_input::set_mouse_curpos() { + int i; + int px = cursor->x - x + panx; + int last_width = 0; + for(i=1; i<=actual_len; i++) { + int sx = inter->font->width(st, i); + if(px < (last_width + (sx-last_width)/2)) { + curpos = i-1; + break; + } + last_width = sx; + } + if(i > actual_len) + curpos = actual_len; +} + +void Zone_text_input::draw() { + int sx; + Zone_panel::draw(); + if(focus) { + int total_x = inter->font->width(st); + sx = inter->font->width(st, curpos); + if(sx - panx > w-8) { + panx = min(panx + (w>>2), total_x - (w-8)); + } + if(sx - panx < 0) { + panx = max(0, panx - (w>>2)); + } + inter->font->draw(st, pan, -panx, 0); + + if(select_start != -1 && select_start != curpos) { + int x1, x2, x3, x4; + if(select_start > curpos) { + x1 = curpos; + x2 = select_start; + } else { + x2 = curpos; + x1 = select_start; + } + x3 = inter->font->width(st, x1)-2; // -2: enleve le 'shrink' ajouter par width() + x4 = inter->font->width(st, x2)-2; // -2: enleve le 'shrink' ajouter par width() + pan->rect(x3-panx, 0, x4-x3+2, inter->font->height(), curcolor); + + char tube_char = st[x2]; + st[x2] = 0; + font_selected->draw(&st[x1], pan, x3-panx, 0); + st[x2] = tube_char; + } + if(focus > 10) { + pan->vline(sx-panx-2, 0, h, curcolor); + pan->vline(sx-panx-1, 0, h, curcolor); + } + } else { + inter->font->draw(st, pan, 0, 0); + } +} + +void Zone_text_input::lost_focus(int cancel) { + focus = 0; + dirt(); + if(cancel) + strcpy(st, val); + else + strcpy(val, st); + input->reraw(); +} + +void Zone_text_input::process() { + Byte c; + if(focus) { + // Support du Clipboard en Doze + check_clipboard(); + for(int i=0; ikey_pending; i++) { + c = input->key_buf[i].c; + if((c == 8) || (c == 127)) { // backspace + if(!cut_selection()) { // si rien de selecter n'a ete couper, + if(curpos > 0) { // procede a un backspace normal + curpos--; + memmove(&st[curpos], &st[curpos+1], actual_len - curpos); + actual_len--; + } + } + continue; + } + if(input->key_buf[i].special) { // touches de deplacement et autres + if(c == 46) {// delete + if(!cut_selection()) { // si rien de selecter n'a ete couper, + if(curpos != actual_len) { // procede a un delete normal + memmove(&st[curpos], &st[curpos+1], actual_len - curpos); + actual_len--; + } + } + } + if(c==37 || c==39 || c==36 || c==35) { + if(input->shift_key & SHIFT) { + if(select_start == -1) + select_start = curpos; + } else { + select_start = -1; + } + } + if(c == 37 && curpos > 0) { // fleche gauche + curpos--; + } + if(c == 39 && curpos != actual_len) { // fleche droite + curpos++; + } + if(c == 36) { // home + curpos = 0; + } + if(c == 35) { // end + curpos = actual_len; + } + continue; + } + if(c == 1) { // CTRL-A (select all) + curpos = actual_len; + select_start = 0; + } + input_char(c); + } + input->key_pending = 0; + focus++; + if(focus > 20) + focus=1; + dirt(); + } +} + +void Zone_text_input::input_char(const Byte c) { + if(c > 31 && c != 183 && c != 127) { + cut_selection(); + memmove(&st[curpos+1], &st[curpos], actual_len-curpos + 1); + st[maxlen] = 0; + if(curpos < maxlen) { + st[curpos] = c; + curpos++; + } + if(actual_len < maxlen) { + actual_len++; + } + if(maxwidth != -1) { + if(inter->font->width(st) > maxwidth) { + actual_len--; + if(curpos > actual_len) + curpos = actual_len; + st[actual_len] = 0; + } + } + } +} + +bool Zone_text_input::cut_selection() { + if(select_start == -1 || select_start == curpos) { + select_start = -1; + return false; + } + int x1, x2; + if(select_start > curpos) { + x1 = curpos; + x2 = select_start; + } else { + x2 = curpos; + x1 = select_start; + } + memmove(&st[x1], &st[x2], actual_len - x2+1); + actual_len = actual_len - (x2-x1); + select_start = -1; + curpos = x1; + return true; +} + +void Zone_text_input::check_clipboard() { +#ifdef UGS_DIRECTX + if(input->shift_key != CONTROL) + return; + if(!input->keys[DIK_V] & PRESSED && !input->keys[DIK_C] & PRESSED && !input->keys[DIK_X] & PRESSED) + return; + if(!OpenClipboard(NULL)) { + skelton_msgbox(" Error opening clipboard.\n"); + return; + } + if(input->keys[DIK_V] & PRESSED) { + input->keys[DIK_V] = 0; + HANDLE h = GetClipboardData(CF_TEXT); + if(h == NULL) { + skelton_msgbox(" Error getting clipboard data.\n"); + } else { + char *clip = (char *) h; + cut_selection(); + int ma = strlen(clip); + for(int i=0; ikeys[DIK_X] & PRESSED) { + input->keys[DIK_X] = 0; + cut = true; + } + if(input->keys[DIK_C] & PRESSED) { + input->keys[DIK_C] = 0; + copy = true; + } + if(copy || cut) { + int x1, x2; + if(select_start > curpos) { + x1 = curpos; + x2 = select_start; + } else { + x2 = curpos; + x1 = select_start; + } + int ma = x2-x1+1; + if(ma > 1) { + HANDLE mem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, ma); + LPVOID buf = GlobalLock(mem); + memcpy((char *) buf, &st[x1], ma-1); + ((char *)buf)[ma-1] = 0; + GlobalUnlock(mem); + HANDLE h = SetClipboardData(CF_TEXT, mem); + if(h == NULL) { + skelton_msgbox(" Error setting clipboard data.\n"); + } else { + if(cut) { + cut_selection(); + } + } + } else { + skelton_msgbox(" nothing selected. aborting.\n",ma); + } + } + CloseClipboard(); +#endif +} + +void Zone_text_input::leaved() { + Zone_panel::leaved(); + high = false; + dirt(); +} + +void Zone_text_input::entered() { + Zone_panel::entered(); + high = true; + dirt(); +} + +Zone_input_numeric::Zone_input_numeric(Inter* in, int *pvar, int ncar, int pmin, int pmax, const Palette &pal, int px, int py, int pw): + Zone_text_input(in, pal, (sprintf(temp_st, "%i",*pvar), temp_st), ncar, px, py, pw) { + /* P.S.: ya-tu une autre facon d'ecrire la ligne si-dessus, ou ben + le C++ c'est vraiment de la marde? */ + var_min = pmin; + var_max = pmax; + num_var = pvar; +} + +void Zone_input_numeric::lost_focus(int cancel) { + if(!cancel) { + int num; + if(sscanf(Zone_text_input::st, "%i", &num) != 1) + num = 0; + if(num >= var_min && num <= var_max) { + *num_var = num; + } else { // si invalide, cancel l'entree + cancel = 1; + } + } + Zone_text_input::lost_focus(cancel); +} + +Zone_text_field::Zone_text_field(Inter* in, int* s, int px, int py, int pw, Font *f2, bool frame): +Zone_panel(in, px, py, pw, in->font->height()+2) { + if(f2 == NULL) + font = in->font; + else + font = f2; + set_val(s); + draw_frame = frame; +} +Zone_text_field::Zone_text_field(Inter* in, const char* s, int px, int py, int pw, Font *f2, bool frame): +Zone_panel(in, px, py, pw, in->font->height()+2) { + if(f2 == NULL) + font = in->font; + else + font = f2; + set_val(s); + draw_frame = frame; +} + +void Zone_text_field::process() { + if(var && val != *var) + set_val(var); +} + +void Zone_text_field::set_val(int* s) { + val = *s; + var = s; + sprintf(st, "%i", val); + dirt(); +} + +void Zone_text_field::set_val(const char* s) { + var = NULL; // c'est une string + if(s) { + strncpy(st, s, sizeof(st)-1); + st[sizeof(st)-1] = 0; + } else { + st[0] = 0; + } + dirt(); +} + +void Zone_text_field::draw() { + Zone_panel::draw(); + if(!draw_frame) { + video->vb->hline(y, x, w, 255); + video->vb->hline(y+h-1, x, w, 0); + video->vb->vline(x, y+1, h-2, 255); + video->vb->vline(x+w-1, y+1, h-2, 0); + video->vb->rect(x+1, y+1, w-2, h-2, 210); + } + if(var) { + font->draw(st, pan, w - font->width(st) - 3, 0); // alignement des numerics a droite + } else { + font->draw(st, pan, 3, 0); // alignement du texte a gauche + } +} + +Zone_clear::Zone_clear(Inter* in, int px, int py, int pw, int ph, int c): +Zone(in, px, py, pw, ph) { + color=c; +} + +void Zone_clear::draw() { + video->vb->rect(x, y, w, h, color); +} + +Inter::Inter() { + first_zone = 0; + font = NULL; + kb_x = kb_y = 0; + kb_active = true; + flush(); +} + +Inter::Inter(Inter *in) { + for(int i=0; inzone(); i++) + add(in->zone[i]); + first_zone = nzone(); + set_font(in->font, false); + kb_active = true; + flush(); +} + +Inter::~Inter() { + flush(); + if(font && del_font) + delete font; +} + +void Inter::set_font(Font* f1, bool del) { + font = f1; + del_font = del; +} + +void Inter::draw_zone() { + int i; +#ifdef UGS_DIRECTX + if(!alt_tab) +#endif + { + if(video->need_paint) { + dirt_all(); + video->need_paint--; + } + for(i=0; idirty && zone[i]->enabled >=0 && !zone[i]->stay_on_top) { + zone[i]->dirty--; + zone[i]->draw(); + } + } + kb_draw_focus(); + for(i=0; ienabled >=0 && zone[i]->stay_on_top) { + zone[i]->draw(); + } + } + } +} + +void Inter::dirt_all() { + for(int i=0; idirt(); +} + +Zone* Inter::do_frame() { + process(); + return clicked; +} + +void Inter::remove(Zone *z) { + for(int i=0; iquel_key = -1; +} + +void Inter::process() { + int i; + clicked = double_clicked = NULL; + for(i = nzone()-1; i >= first_zone; i--) + if(zone[i]->enabled >=0) + zone[i]->process(); + + if(kb_visible && !kb_focus) { + kb_focus = kb_find_closest(); + if(kb_focus) + tag(kb_focus); + } + + if(focus) { + int lost=-1; + if(input->quel_key == KEY_ESCAPE) + lost=1; + if(input->quel_key == KEY_ENTER || input->quel_key == KEY_PADENTER) + lost=0; + if(lost != -1) { + focus->lost_focus(lost); + if(!kb_visible && in != focus) + focus->leaved(); + focus = NULL; + input->quel_key = -1; + } + } else { + // stuff pour keyboard control + if(!kb_visible) { + if(kb_focus != NULL) { // si une zone etait focuser et que le kb_visible est devenu faux + de_tag(kb_focus); // il faut la detagger. + kb_focus = NULL; + } + if(kb_active) { + if(kb_check_key(KEY_DOWNARROW) || kb_check_key(KEY_UPARROW) || + kb_check_key(KEY_LEFTARROW) || kb_check_key(KEY_RIGHTARROW) || + kb_check_key(KEY_TAB)) { + kb_focus=NULL; + if(in) { + if(in->kb_focusable) + kb_focus = in; + else if(in->parent && in->parent->kb_focusable) { + kb_focus = in->parent; + } + } + if(!kb_focus) + kb_focus = kb_find_upmost(); + if(kb_focus) { // si une zone 'focusable' dans toute l'interface + last_mouse_x = cursor->x; + last_mouse_y = cursor->y; + kb_anim = 0; + in = NULL; + tag(kb_focus); + kb_visible = true; + if(cursor) + cursor->visible = false; + } + input->quel_key = -1; + } + } + } else { + if(last_mouse_x != cursor->x || last_mouse_y != cursor->y || alt_tab) { + // la souris a bouge: enleve le kb_focus + kb_visible = false; + if(kb_focus) { + de_tag(kb_focus); + kb_focus = NULL; + } + } else { + if(!kb_focus) { + kb_focus = kb_find_closest(); + } + if(kb_focus && (kb_focus->enabled < 0 || !kb_focus->kb_focusable)) { // si la zone keyboard a ete disabler ou n'est plus kb_focusable + de_tag(kb_focus); + kb_focus = kb_find_closest(); + if(kb_focus) + tag(kb_focus); + } + } + if(kb_visible) { + bool bouge = false; + Zone *temp = NULL; + if(kb_check_key(KEY_DOWNARROW)) { + temp = kb_find_down(); + bouge = true; + } + if(kb_check_key(KEY_TAB)) { + temp = kb_find_next(); + bouge = true; + } + if(kb_check_key(KEY_TAB) && input->shift_key & SHIFT) { + temp = kb_find_prev(); + bouge = true; + } + if(kb_check_key(KEY_UPARROW)) { + temp = kb_find_up(); + bouge = true; + } + if(kb_check_key(KEY_RIGHTARROW)) { + temp = kb_find_right(); + if(temp) + bouge = true; + } + if(kb_check_key(KEY_LEFTARROW)) { + temp = kb_find_left(); + if(temp) + bouge = true; + } + if(bouge) { + if(temp && temp != kb_focus) { + de_tag(kb_focus); + tag(temp); + kb_focus = temp; + } + input->quel_key = -1; + } + + if(input->quel_key == KEY_ENTER || input->quel_key == KEY_PADENTER || kb_check_key(KEY_SPACE)) { + if(kb_focus) { + select_zone(kb_focus, 0); + input->quel_key = -1; + } + } + } + } + } + if(cursor && !cursor->visible) { + if(last_mouse_x != cursor->x || last_mouse_y != cursor->y) { + // la souris a bouge: refait apparaitre le curseur + cursor->visible = true; + } + } + + if(!kb_visible) { + for(i=nzone()-1; i >= first_zone; i--) { + if(zone[i]->in() && zone[i]->enabled >=0) { + if(focus) { + if(input->mouse.quel != -1) { + if(focus != zone[i]) { + focus->lost_focus(0); + if(in != focus) + focus->leaved(); + focus = NULL; + } + } + } + if(!focus || !in) { + if(in != zone[i]) { + if(in) + de_tag(in); + in = zone[i]; + tag(in); + } + } + if(in->parent) + in->parent->waiting(); + else + in->waiting(); + if(input->mouse.quel != -1) { + select_zone(zone[i], input->mouse.quel); + } + break; + } + } + } + if(double_click_delay) + double_click_delay--; +} + +void Inter::de_tag(Zone *z) { + if(z->parent) + z->parent->leaved(); + else + z->leaved(); +} + +void Inter::tag(Zone *z) { + if(z->parent) + z->parent->entered(); + else + z->entered(); + kb_x = z->x; + kb_y = z->y; +} + +void Inter::kb_deactivate() { + kb_active = false; + kb_visible = false; +} + +void Inter::kb_reactivate() { + kb_active = true; +} + +void Inter::kb_alloc_key(const int i) { + kb_keys.add(i); +} + +void Inter::kb_free_key(const int i) { + kb_keys.remove_item(i); +} + +bool Inter::kb_check_key(const int i) const { + if(input->quel_key == i) { + for(int j=0; j= first_zone; i--) + if(zone[i]->enabled >=0 && zone[i]->kb_focusable) + if(zone[i]->y < best_y) { + best = zone[i]; + best_y = zone[i]->y; + } + return best; +} + +Zone *Inter::kb_find_downmost() { + int best_y = -1; + Zone *best = NULL; + for(int i = nzone()-1; i >= first_zone; i--) + if(zone[i]->enabled >=0 && zone[i]->kb_focusable) + if(zone[i]->y > best_y) { + best = zone[i]; + best_y = zone[i]->y; + } + return best; +} + +Zone *Inter::kb_find_down() { + int best_y = 9999999, dist; + Zone *best = NULL; + for(int i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + if(z->y > kb_y && (abs(z->x - kb_x)>>1) < z->y - kb_y) { + dist = (z->x - kb_x)*(z->x - kb_x) + (z->y - kb_y)*(z->y - kb_y); + if(dist < best_y) { + best = z; + best_y = dist; + } + } + } + return best; +} + +Zone *Inter::kb_find_up() { + int best_y = 9999999, dist; + Zone *best = NULL; + for(int i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + if(z->y < kb_y && (abs(z->x - kb_x)>>1) < kb_y - z->y) { + dist = (z->x - kb_x)*(z->x - kb_x) + (z->y - kb_y)*(z->y - kb_y); + if(dist < best_y) { + best = z; + best_y = dist; + } + } + } + return best; +} + +Zone *Inter::kb_find_right() { + int best_y = 9999999, dist; + Zone *best = NULL; + for(int i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + if(z->x > kb_x && (abs(z->y - kb_y)>>1) < z->x - kb_x) { + dist = (z->x - kb_x)*(z->x - kb_x) + (z->y - kb_y)*(z->y - kb_y); + if(dist < best_y) { + best = z; + best_y = dist; + } + } + } + return best; +} + +Zone *Inter::kb_find_left() { + int best_y = 9999999, dist; + Zone *best = NULL; + for(int i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + if(z->x < kb_x && (abs(z->y - kb_y)>>1) < kb_x - z->x) { + dist = (z->x - kb_x)*(z->x - kb_x) + (z->y - kb_y)*(z->y - kb_y); + if(dist < best_y) { + best = z; + best_y = dist; + } + } + } + return best; +} + +Zone *Inter::kb_find_closest() { + int best_y = 9999999, dist; + Zone *best = NULL; + for(int i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) { + dist = (z->x - kb_x)*(z->x - kb_x) + (z->y - kb_y)*(z->y - kb_y); + if(dist < best_y) { + best = z; + best_y = dist; + } + } + } + return best; +} + +Zone *Inter::kb_find_prev() { + int i; + int debut =nzone()-1; + + // trouve la zone actuellement focuser... + for(i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z == kb_focus) { + debut = i; + break; + } + } + // ensuite trouve la 'previous' zone focusable (a partir de celle trouve) + for(i = debut-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + return z; + } + // si rien de bon, repart de la fin (pour looper) + for(i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + return z; + } + return NULL; // si rien pentoute +} + +Zone *Inter::kb_find_next() { + int i; + int debut =0; + + // trouve la zone actuellement focuser... + for(i = nzone()-1; i >= first_zone; i--) { + Zone *z = zone[i]; + if(z == kb_focus) { + debut = i; + break; + } + } + // ensuite trouve la 'next' zone focusable (a partir de celle trouve) + for(i = debut+1; i < nzone(); i++) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + return z; + } + // si rien de bon, repart du debut (pour looper) + for(i = first_zone; i < nzone(); i++) { + Zone *z = zone[i]; + if(z->enabled >=0 && z->kb_focusable) + return z; + } + return NULL; // si rien pentoute +} + +void Inter::select_zone(Zone *z, int quel) { + if(focus) { + if(focus != z) { + focus->lost_focus(0); + if(in != focus) + focus->leaved(); + focus = NULL; + } + } + if(z->focusable) { + focus = z; + input->key_pending = 0; + } + if(z->parent) { + z->parent->clicked(quel); + clicked = z->parent; + } else { + if(z->child) + if(z->child->focusable) { + focus = z->child; + input->key_pending = 0; + } + clicked = z; + z->clicked(quel); + } + + // double-click (TODO: ne supporte pas les 'child' Zone) + if(double_clicked_first == z && double_click_delay > 0) { + double_clicked = z; + double_clicked_first = NULL; + z->double_clicked(); + } else { + double_clicked_first = z; + double_click_delay = 70; // delai fix de 70/100 de seconde + } + + if(kb_visible && kb_focus != z) { + if(kb_focus) + de_tag(kb_focus); + kb_focus = z; + tag(kb_focus); + } + input->mouse.quel = -1; // clear pour empecher multiple click dans la meme frame +} + +void Inter::kb_draw_focus() { + if(kb_visible && kb_focus) { + int x,y,w,h; + x = kb_focus->x; + y = kb_focus->y; + w = kb_focus->w; + h = kb_focus->h; + /*video->vb->hline(y, x, w, 255); + video->vb->hline(y+h-1, x, w, 255); + video->vb->vline(x, y, h, 255); + video->vb->vline(x+w-1, y, h, 255);*/ + //kb_anim++; + int seed = kb_anim & 7; + bool draw; + if(kb_anim & 8) + draw = true; + else + draw = false; + int side = 0; + int x2 = x; + int y2 = y; + do { + switch(side) { + case 0: x2++; + if(x2 >= x+w) { + x2 = x+w-1; + side++; + } + break; + case 1: y2++; + if(y2 >= y+h) { + y2 = y+h-1; + side++; + } + break; + case 2: x2--; + if(x2 < x) { + x2 = x; + side++; + } + break; + case 3: y2--; + if(y2 < y) { + y2 = y; + side++; + } + break; + } + if(draw) + video->vb->put_pel(x2, y2, 255); + seed++; + //if(seed == 8) { + draw = !draw; + seed = 0; + //} + } while(x2 != x || y2 != y); + } +} + diff --git a/skelton/common/listbox.cpp b/skelton/common/listbox.cpp new file mode 100644 index 0000000..e784d4f --- /dev/null +++ b/skelton/common/listbox.cpp @@ -0,0 +1,343 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif + +#include "input.h" +#include "cursor.h" +#include "listbox.h" + +Zone_listbox::Zone_listbox(Inter* in, Bitmap *fond, Font *f, int *pval, int px, int py, int pw, int ph): + Zone_watch_int(in, pval, px, py, pw, ph) { + if(fond) + back = new Bitmap((*fond)[py+1]+px+1, pw-2, ph-2, fond->realwidth); + else + back = NULL; + screen = Video_bitmap::New(px+1, py+1, pw-2, ph-2); + font2 = f; + zup = new Zone_listup(this); + zdown = new Zone_listdown(this); + for(int i=y+18; iheight(); i+=f->height()) { + list.add(new Zone_listtext(this, i)); + } + first_item = 0; + if(val) + select(*val); +} + +Zone_listbox::~Zone_listbox() { + empty(); + while(list.size()) { + delete list.last(); + list.removelast(); + } + delete zdown; + delete zup; + if(back) + delete back; + delete screen; +} + +void Zone_listbox::draw() { + screen->setmem(); + if(back) + back->draw(screen, 0, 0); + video->vb->hline(y, x, w, 210); + video->vb->hline(y+h-1, x, w, 210); + video->vb->vline(x, y, h, 210); + video->vb->vline(x+w-1, y, h, 210); + //video->vb->hline(y+20, x, w, 210); + //video->vb->hline(y+h-1-20, x, w, 210); +} + +void Zone_listbox::dirt() { + if(dirty != 2) { + Zone_watch_int::dirt(); + zup->dirt(); + zdown->dirt(); + for(int i=0; idirt(); + } +} + +void Zone_listbox::enable() { + Zone_watch_int::enable(); + zup->enable(); + zdown->enable(); + for(int i=0; ienable(); +} + +void Zone_listbox::disable() { + Zone_watch_int::disable(); + zup->disable(); + zdown->disable(); + for(int i=0; idisable(); +} + +void Zone_listbox::init_sort() { + sort_list.clear(); +} + +void Zone_listbox::add_sort(Listable *l) { + sort_list.add(l); +} + +void Zone_listbox::end_sort() { + qsort((void *) &sort_list[0], sort_list.size(), sizeof(sort_list[0]), compare_sort); + for(int i=0; ilist_name; + char *s2 = (*(Listable **) arg2)->list_name; + return stricmp(s1, s2); +} + + +void Zone_listbox::add_item(Listable *e) { + elements.add(e); + sync_list(); +} + +void Zone_listbox::replace_item(int i, Listable *e) { + delete elements[i]; + elements.replace(i, e); + sync_list(); +} + +void Zone_listbox::remove_item(Listable *e) { + int i; + for(i=0; ix > x && cursor->x < x+w && cursor->y > y && cursor->y < y+h) { + int z = input->mouse.dz; + if(z > 0) + zup->clicked(0); + if(z < 0) + zdown->clicked(0); + } + } +} + +int Zone_listbox::search(Listable *source) { + for(int i=0; iis_equal(source)) + return i; + return -1; +} + +bool Zone_listbox::in_listbox(const Zone *z) { + for(int i=0; ifont; + list[i]->kb_focusable = false; + if(i+first_item >= elements.size()) { + list[i]->set_text(""); + } else { + if(val) + list[i]->kb_focusable = true; + Listable *li = elements[i+first_item]; + list[i]->set_text(li->list_name); + if(li->font) + f = li->font; + } + list[i]->set_font(f); + } + if(val) + select(*val); + dirt(); +} + +void Zone_listbox::empty() { + while(elements.size()) { + delete elements.last(); + elements.removelast(); + } +} + +void Zone_listbox::clear() { + empty(); + first_item = 0; + if(val) + *val = -1; + sync_list(); +} + +void Zone_listbox::unselect() { + if(!val) + return; + if(*val >= first_item && *val < first_item+list.size()) { + Font *f = inter->font; + if(elements[*val]->font) + f = elements[*val]->font; + list[*val-first_item]->set_font(f); + } + *val = -1; +} + +void Zone_listbox::select(int q) { + if(!val) + return; + *val = q; + if(*val >= first_item && *val < first_item+list.size()) { + list[*val-first_item]->set_font(font2); + } +} + +Zone_listupdown::Zone_listupdown(Zone_listbox *par, const char *s, int py): + Zone_text_select(par->inter, par->font2, s, par->x, py, par->w) { + parent = par; + leaved(); + kb_focusable = true; +} + +void Zone_listupdown::waiting() { + Zone_text_select::waiting(); + if(input->mouse.button[0] & PRESSED) { + count--; + if(count < 0) { + clicked(0); + count = 5; + } + } +} + +void Zone_listupdown::leaved() { + Zone_text_select::leaved(); + count = 40; +} + +void Zone_listupdown::dirt() { + Zone_text_select::dirt(); + parent->dirt(); +} + +Zone_listup::Zone_listup(Zone_listbox *par): + Zone_listupdown(par, "·3", par->y) { +} + +void Zone_listup::clicked(int quel) { + if(parent->first_item > 0) { + parent->first_item--; + parent->sync_list(); + parent->clicked(quel); + } +} + +Zone_listdown::Zone_listdown(Zone_listbox *par): + Zone_listupdown(par, "·4", par->y+par->h-18) { +} + +void Zone_listdown::clicked(int quel) { + if(parent->first_item < parent->elements.size() - parent->list.size()) { + parent->first_item++; + parent->sync_list(); + parent->clicked(quel); + } +} + +Listable::Listable(const char *s, Font *f) { + strcpy(list_name, s); + font = f; +} + +bool Listable::is_equal(Listable *source) { + return !strcmp(list_name, source->list_name); +} + +Zone_listtext::Zone_listtext(Zone_listbox *par, int i): + Zone_text(par->inter, "", par->x+2, i, par->w-4) { + parent = par; + quel = par->list.size(); + high = false; +} + +void Zone_listtext::clicked(int quel) { + //Watch out! Param 'quel' is mouse button; this->quel is... + // hmmm... something else... Ask Remz + parent->unselect(); + if(this->quel < parent->elements.size()) { + parent->select(this->quel + parent->first_item); + //inter->clicked = parent; // yark! + parent->clicked(quel); + } +} + +void Zone_listtext::draw() { + parent->screen->setmem(); + font->draw(st, parent->screen, text_x-parent->x, y-parent->y); + if(high) { + if(!kb_focusable) + high=false; + else + video->vb->box(x, y, w, h, 255); + } +} + +void Zone_listtext::dirt() { + Zone_text::dirt(); + parent->dirt(); +} + +void Zone_listtext::entered() { + Zone_text::entered(); + if(parent->val && kb_focusable) { + // kb_focusable indique en meme temps que cette zone_listtext contient + // actuellement quelque chose + high=true; + dirt(); + } +} + +void Zone_listtext::leaved() { + Zone_text::leaved(); + if(parent->val && kb_focusable) { + // kb_focusable indique en meme temps que cette zone_listtext contient + // actuellement quelque chose + high=false; + dirt(); + } +} diff --git a/skelton/common/net.cpp b/skelton/common/net.cpp new file mode 100644 index 0000000..15e0443 --- /dev/null +++ b/skelton/common/net.cpp @@ -0,0 +1,1396 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifdef UGS_DIRECTX +typedef int addr_size_t; +#endif + +#include + +#ifdef UGS_LINUX +#ifdef SOCKS +#include +#undef read +#endif +#include +#ifndef __STRICT_ANSI__ +#define I_SET_STRICT_ANSI +#define __STRICT_ANSI__ +#endif +#include +#ifdef I_SET_STRICT_ANSI +#undef I_SET_STRICT_ANSI +#undef __STRICT_ANSI__ +#endif +#include +#include +#include +#include +#include +#include +typedef size_t addr_size_t; +inline int closesocket(int fd) { + return close(fd); +} +#endif + +#include +#include "types.h" +#include "utils.h" +#include "main.h" +#include "error.h" +#include "buf.h" +#include "net_buf.h" +#include "http_request.h" +#include "net.h" + +IP_addr::IP_addr(const IP_addr& o) { + set(o.ip, o.mask); +} + +IP_addr::IP_addr(const char *addr) { + set(addr); +} + +IP_addr::IP_addr(Dword pip, Dword pmask) { + set(pip, pmask); +} + +bool IP_addr::operator>(const IP_addr& o) { + Dword m=mask&o.mask; + if(m!=mask) + return false; + return (ip&m)==(o.ip&m); +} + +bool IP_addr::operator>=(const IP_addr& o) { + return *this>o || (ip==o.ip && mask==o.mask); +} + +void IP_addr::print(char *st) { + st[0]=0; + char num[4]; + Dword ip=this->ip; + Dword mask=this->mask; + int i; + for(i=0; i<4; i++) { + if((mask&0xFF000000)==0) + strcat(st, "*"); + else { + sprintf(num, "%i", ip>>24); + strcat(st, num); + } + if(i<3) + strcat(st, "."); + ip<<=8; + mask<<=8; + } +} + +void IP_addr::set(const char *addr) { + Byte a[4]; + Byte m[4]; + const char *p=addr; + int i; + for(i=0; i<4; i++) { + const char *dot=strchr(p, '.'); + if(!dot) + dot=p+strlen(p); + char num[4]; + if(dot-p>4) + break; + memcpy(num, p, dot-p); + num[dot-p]=0; + int part=1; + if(num[0]==0 || !strcmp(num, "*")) + m[i]=0; + else { + m[i]=255; + part=atoi(num); + } + if(part<0 || part>255) + break; + a[i]=(Byte)part; + if(*dot) + p=dot+1; + else + p=dot; + } + if(i==4) { + ip=a[0]<<24 | a[1]<<16 | a[2]<<8 | a[3]; + mask=m[0]<<24 | m[1]<<16 | m[2]<<8 | m[3]; + } + else { + ip=0; + mask=0xFFFFFFFF; + } +} + +void IP_addr::set(Dword pip, Dword pmask) { + ip=pip; + mask=pmask; +} + +bool Net_param::accept_connection(Net_connection *nc) { + return net->connections.size()<64; +} + +int Net_connection::next_port=65536; + +Net_connection::Net_connection() { + _state=invalid; + destport=next_port++; + packet_based=true; + joined=false; + trusted=false; + incoming=NULL; + buf=new Buf(0, 256); + connected_to=NULL; + incoming_inactive=outgoing_inactive=0; + outgoing_size=0; + outgoing_min=0; + outgoing_max=0; + outgoing_total=0; + incoming_size=0; + incoming_min=0; + incoming_max=0; + incoming_total=0; + commit_count_in=0; + commit_count_out=0; + commit_count_both=0; + commit_count_total=0; +} + +Net_connection::~Net_connection() { + disconnect(); + if(buf) + delete buf; + if(incoming) + delete incoming; +} + +Net_connection::Netstate Net_connection::state() { + return _state; +} + +void Net_connection::connect(Net_connection *dest) { + if(connected_to || dest->connected_to) { + skelton_msgbox("Net_connection::connect: already connected!\n"); + return; + } + connected_to=dest; + dest->connected_to=this; + _state=connected; + dest->_state=connected; +} + +void Net_connection::connect(Dword addr, int port) { +} + +void Net_connection::connect(const char* host, int port) { +} + +bool Net_connection::checktcp() { + if(_state!=connected) + return false; + if(buf->size()>=sizeof(Word)) { + Word s = *(Word *) buf->get(); + if(buf->size()>=sizeof(Word)+s) + return true; + } + return false; +} + +void Net_connection::receivetcp(Net_buf *p) { + if(!packet_based) + return; + p->from = this; + p->from_addr = INADDR_LOOPBACK; + Word size = *(Word *) buf->get(); + memcpy(p->buf, buf->get()+sizeof(Word), size); + buf->remove_from_start(size+sizeof(Word)); + incoming_inactive=0; + incoming_size+=size+sizeof(Word); +} + +int Net_connection::receivetcp(Byte *buf, Dword size) { + if(_state!=connected || packet_based) + return false; + if(size > this->buf->size()) + size=this->buf->size(); + memcpy(buf, this->buf->get(), size); + this->buf->remove_from_start(size); + if(size) { + incoming_inactive=0; + incoming_size+=size; + } + return size; +} + +void Net_connection::sendtcp(Packet *p2) { + if(_statewrite(&p); + Word size=p.len(); + static Byte outbuf[1026]; + *(Word *) outbuf=size; + memcpy(&outbuf[2], p.buf, size); + sendtcp(outbuf, size+2); +} + +void Net_connection::sendtcp(const Byte *buf, Dword size) { + if(connected_to) { + connected_to->buf->append(buf, size); + outgoing_inactive=0; + outgoing_size+=size; + } +} + +void Net_connection::commit() { + if(outgoing_size && incoming_size) + commit_count_both++; + if(outgoing_size) + commit_count_out++; + if(outgoing_size>outgoing_max) + outgoing_max=outgoing_size; + if(outgoing_size && (outgoing_sizeincoming_max) + incoming_max=incoming_size; + if(incoming_size && (incoming_size_state=disconnected; + connected_to->connected_to=NULL; + connected_to=NULL; + } +} + +Dword Net_connection::getbufsize() const { + return buf->size(); +} + +//Constructeur pour client_connection et server_connection +Net_connection_tcp::Net_connection_tcp(int p, bool ppacket_based) { + outgoing_buf.reserve(1024); + packet_based=ppacket_based; + joined=false; + trusted=false; + incoming=NULL; + _state=invalid; + desthost[0]=0; + destaddr=0; + destport=0; + tcpbufsize=0; + tcppacsize=0; + + tcpsock = socket(PF_INET, SOCK_STREAM, 0); + if(net->getlasterror(tcpsock)) { + return; + } + + int val=1; +/* if(net->getlasterror(setsockopt(tcpsock, SOL_SOCKET, SO_KEEPALIVE, (char *) &val, sizeof(int)))) + return;*/ + + val=1; + if(net->getlasterror(setsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(int)))) + return; + +#ifdef UGS_DIRECTX + unsigned long val2=1; + if(net->getlasterror(ioctlsocket(tcpsock, FIONBIO, &val2))) + return; +#endif +#ifdef UGS_LINUX + if(net->getlasterror(fcntl(tcpsock, F_SETFL, fcntl(tcpsock, F_GETFL)|O_NONBLOCK))) + return; + + if(net->getlasterror(setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(int)))) + return; +#endif + + tcpsin.sin_family = AF_INET; + tcpsin.sin_addr.s_addr = htonl(0); + tcpsin.sin_port = htons(p); + + if(p) { + if(net->getlasterror(bind(tcpsock, (sockaddr *) &tcpsin, sizeof(tcpsin)))) + return; + + if(net->getlasterror(listen(tcpsock, 5))) + return; + } + + from=0; + _state=valid; + skelton_msgbox("Opening TCP socket %i\n",tcpsock); +} + +//Constructeur pour connections acceptées +Net_connection_tcp::Net_connection_tcp(int sock, Dword adr, int port, bool ppacket_based) { + outgoing_buf.reserve(1024); + packet_based=ppacket_based; + joined=false; + trusted=false; + incoming=NULL; + _state=invalid; + destaddr=adr; + destport=port; + desthost[0]=0; + skelton_msgbox("Opening TCP socket (accept) %i\n",sock); + tcpsock=sock; + #ifdef UGS_DIRECTX + int val=1; + if(net->getlasterror(setsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(int)))) + skelton_msgbox("Error setting TCP_NODELAY for accepted socket %i, ignoring.\n",tcpsock); + #endif + tcpbufsize=0; + tcppacsize=0; + + tcpsin.sin_family = AF_INET; + tcpsin.sin_addr.s_addr = htonl(adr); + tcpsin.sin_port = htons(port); + from=adr; + + _state=connected; +} + +Net_connection_tcp::~Net_connection_tcp() { + if(_state==dnslookup) + net->gethostbyname_cancel(); + if(tcpsock != -1) { + char buf[1024]; + skelton_msgbox("Shutting down..."); + if(shutdown(tcpsock, 1) != SOCKET_ERROR) { + while(net->checkreceive(tcpsock) > 0) { + int i = recv(tcpsock, buf, 1024, 0); + if(i <= 0) + break; + skelton_msgbox("%i bytes,", i); + } + skelton_msgbox("Done!\n"); + } else { + skelton_msgbox("Ignoring shutdown error.\n"); + } + skelton_msgbox("Closing TCP socket %i...\n", tcpsock); + closesocket(tcpsock); + } +} + +void Net_connection_tcp::connect(Dword adr, int port) { + desthost[0]=0; + destaddr=adr; + destport=port; + from=adr; + tcpsin.sin_addr.s_addr = htonl(adr); + tcpsin.sin_port = htons(port); + skelton_msgbox("Net_connection_tcp::connect: Connecting socket to %s: %i... ", inet_ntoa(tcpsin.sin_addr), port); + _state=connecting; + if(net->getlasterror(::connect(tcpsock, (sockaddr *) &tcpsin, sizeof(tcpsin)))) { + msgbox("Failed [%s]\n", net->last_error); + _state=invalid; + return; + } + skelton_msgbox("done!\nChecking socket... "); + if(net->checkreceive(tcpsock) >= 0) { + skelton_msgbox("done!\n"); + } +} + +void Net_connection_tcp::connect(const char* host, int port) { + if(strlen(host)name_handle==0) { +#endif /* UGS_DIRECTX */ + destaddr = net->getaddress(host); + if(net->port_resolve) // si 'host' contient un port genre "host:port" + destport = net->port_resolve; // override le port en parametre + msgbox("Net_connection_tcp::connect: host=%s, destaddr=%x port=%i\n", host, destaddr, destport); + if(destaddr) { // si resou immediatement + connect(destaddr, destport); + return; + } + _state=dnslookup; +#ifdef UGS_DIRECTX + } + else + _state=waitingfordns; +#endif /* UGS_DIRECTX */ +} + +bool Net_connection_tcp::checktcp() { + if(_statecheckreceive(tcpsock) > 0 && tcpbufsize<1024) { + int temp = recv(tcpsock, (char *) &tcpbuf[tcpbufsize], 1024-tcpbufsize, 0); + // reset by remote side ! + net->getlasterror(temp); + char *msg = net->failed(); + if(msg) { + skelton_msgbox("Net_connection_tcp::checktcp: Socket error on connection #%i. Shutting it.\n[%s]\n", tcpsock, msg); + _state=disconnected; + return false; + } + tcpbufsize+=temp; + if(!temp) { + //Connection closed gracefully + _state=disconnected; + return false; + } + incoming_inactive=0; + incoming_size+=temp; + } + if(tcpbufsize>=sizeof(Word)) { + Word pacsize=*(Word *)tcpbuf; + if(!pacsize || (pacsize >= 1024 && pacsize != (('/'*256) +'/'))) { + skelton_msgbox("Garbage received on connection #%i (%04X). Shutting it.\n", tcpsock, pacsize); + _state=disconnected; // forcing a graceful shutdown + return false; + } + if(pacsize==(('/'*256) +'/')) { + skelton_msgbox("Net_connection_tcp::checktcp: connection #%i reverted to packet_based==false.\n", tcpsock); + packet_based=false; + incoming=new Buf(0, 256); + if(tcpbufsize>sizeof(Word)) + incoming->append(tcpbuf+sizeof(Word), tcpbufsize-sizeof(Word)); + tcpbufsize=0; + char login[512]; + sprintf(login, "%s\r\n\r\n", net->net_param->get_motd()); + sendtcp((Byte *)login, strlen(login)); + return false; + } + if(tcpbufsize>=pacsize+sizeof(Word)) { + tcppacsize=pacsize; + /* + skelton_msgbox("Recv %i bytes (TCP): ", tcppacsize+sizeof(Word)); + for(Dword i=0; ifrom = this; + p->from_addr = from; + memcpy(p->buf, tcpbuf+sizeof(Word), tcppacsize); + memmove(tcpbuf, tcpbuf+sizeof(Word)+tcppacsize, 1024-(tcppacsize+sizeof(Word))); + tcpbufsize-=tcppacsize+sizeof(Word); + tcppacsize=0; +} + +int Net_connection_tcp::receivetcp(Byte *buf, Dword size) { + if(_statecheckreceive(tcpsock) > 0) { + int temp = recv(tcpsock, (char *) buf, size, 0); + // reset by remote side ! + net->getlasterror(temp); + char *msg = net->failed(); + if(msg) { + char st[64]; + net->stringaddress(st, address(), getdestport()); + skelton_msgbox("Net_connection_tcp::receivetcp: Socket error on connection %s.\n[%s]\n", st, msg); + _state=disconnected; + temp = 0; + } + if(!temp) { + //Connection closed gracefully + _state=disconnected; + } + if(temp) + incoming_inactive=0; + incoming_size+=temp; + return temp; + } + return 0; +} + +void Net_connection_tcp::sendtcp(Packet *p2) { + if(_statewrite(&p); + Word size=p.len(); + static Byte outbuf[1026]; + *(Word *) outbuf=size; + memcpy(&outbuf[2], p.buf, size); + sendtcp(outbuf, size+2); +} + +void Net_connection_tcp::sendtcp(const Byte *buf, Dword size) { + static Net_connection_tcp *test=NULL; + test=this; + outgoing_inactive=0; + outgoing_buf.append(buf, size); + outgoing_size+=size; +} + +void Net_connection_tcp::commit() { + if(_state!=connected) + return; + Net_connection::commit(); //Only for stats accounting + Dword size=outgoing_buf.size(); + if(!size) + return; + Dword temp = send(tcpsock, (const char *)(outgoing_buf.get()), size, 0); + outgoing_buf.resize(0); + if(net->getlasterror(temp)) { + char st[64]; + net->stringaddress(st, address(), getdestport()); + skelton_msgbox("Net_connection_tcp::commit: '%s', closing %s.\n", net->last_error, st); + _state=disconnected; + return; + } + if(temp != size) { + char st[64]; + net->stringaddress(st, address(), getdestport()); + skelton_msgbox("Net_connection_tcp::commit: sent %i bytes but packet was %i! Closing %s.\n", temp, size, st); + _state=disconnected; + return; + } +} + +Net_connection::Netstate Net_connection_tcp::state() { + if(_statename_handle==0) { +#endif /* UGS_DIRECTX */ + Dword a = net->getaddress(desthost); + if(a) { + connect(a, destport); + return _state; +#ifdef UGS_DIRECTX + } +#endif /* UGS_DIRECTX */ + _state=dnslookup; + } + return _state; + } + if(_state==dnslookup) { +#ifdef UGS_DIRECTX + if(net->name_handle==0) { +#endif /* UGS_DIRECTX */ + if(net->name_resolve==0) { + net->gethostbyname_cancel(); + _state=disconnected; + return _state; + } else { + connect(net->name_resolve, destport); + return _state; + } +#ifdef UGS_DIRECTX + } +#endif /* UGS_DIRECTX */ + return _state; + } + fd_set fdsoc; + FD_ZERO(&fdsoc); + FD_SET(tcpsock, &fdsoc); + + timeval empty_tv; + empty_tv.tv_sec = 0; + empty_tv.tv_usec = 0; + int winsock_pourriture=0; + for(;;) { + int i = select(tcpsock+1, NULL, &fdsoc, NULL, &empty_tv); + if(i > 0) { + winsock_pourriture++; + if(winsock_pourriture > 5) { + skelton_msgbox(" ok it passed the test!\n"); + _state=connected; + return _state; + } + } else { + net->getlasterror(i); + if(net->last_error) { + skelton_msgbox("Net_connection_tcp::state(): socket failed [%s]\n", net->last_error); + _state=disconnected; + } + if(i==0) + skelton_msgbox("Net_connection_tcp::state(): socket %i, state=%i, is awaiting writability...\n", tcpsock, _state); + return _state; + } + } +} + +int Net_connection_tcp::getFD() const { + return tcpsock; +} + +Net::Net(Net_param *np) { + active = false; + net_param=np; + client_connection=server_connection=NULL; + last_error = NULL; + + name_resolve = (Dword)-1; + port_resolve = -1; + host_name[0] = 0; + udpnum = 0; + udpport=-1; +#ifdef UGS_DIRECTX + name_buf[0] = 0; + name_handle = 0; + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(1, 1); + if(checkerror(WSAStartup(wVersionRequested, &wsaData))) + return; + + if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { + last_error = "Wrong version of Windows Sockets DLL"; + return; + } +#endif + + active = true; + init_local_addresses(); +} + +Net::~Net() { + if(active) + gethostbyname_cancel(); + close_all_udp(); + stop_client(); + stop_server(); + if(net_param) + delete net_param; + if(callbacks.size() != 0) + skelton_msgbox("Net::~Net: callback size should be 0, but it's %i\n", callbacks.size()); +} + +void Net::init_local_addresses() { + if(!active) + return; + if(getlasterror(gethostname(host_name, 1024))) { + skelton_msgbox("Net::Net: gethostname() failed. Ignoring\n"); + host_name[0] = 0; + } + else { + skelton_msgbox("Net::Net: gethostname() is %s\n", host_name); + // trouve la liste des interfaces IP du cok en cours + struct hostent *host; + Dword adr = 0; + + host = gethostbyname(host_name); + if(host) { + Dword *s = (Dword *) *host->h_addr_list; + while(*(char *) s && (char *) s < host->h_name) { + adr = ntohl(*s); + host_adr.add(adr); + s++; + } + } + else { + skelton_msgbox(" gethostbyname() failed. Ignoring.\n"); + } + } +} + +void Net::init_all_udp() { + if(!active) + return; + udpport = net_param->udpport(); + for(int i=0; iincoming_inactive++; + connections[i]->outgoing_inactive++; + } + if(!loop_only) { + //Accept pending connections + while(accept()) + ; + } + while(true) { + bool done=true; + fd_set fdsoc; + FD_ZERO(&fdsoc); + int maxsock=0; + #define ADD_SOCK(sock) { \ + FD_SET(sock, &fdsoc); \ + if(sock>maxsock) \ + maxsock=sock; \ + } + + for(i=0; istate()==Net_connection::connected) { + int tcpsock=connections[i]->getFD(); + if(tcpsock==-1) { + if(connections[i]->checktcp()) + done=false; + } + else + if(!loop_only) + ADD_SOCK(tcpsock); + } + if(client_connection && client_connection->state()==Net_connection::connected) { + int tcpsock=client_connection->getFD(); + if(tcpsock==-1) { + if(client_connection->checktcp()) + done=false; + } + else + if(!loop_only) + ADD_SOCK(tcpsock); + } + if(!loop_only) + for(i=0; igetFD(); + if(tcpsock==-1 || FD_ISSET(tcpsock, &fdsoc)) { + //There's stuff to read from client_connection->tcpsock + while(client_connection && client_connection->checktcp()) { + Net_buf nb; + client_connection->receivetcp(&nb); + packetreceived(&nb, true); + } + verify_server_connection(); // verify if server has been disconnected + } + } + int co; + for(co=0; cogetFD(); + if(tcpsock==-1 || FD_ISSET(tcpsock, &fdsoc)) + isset=true; + if(nc->state()==Net_connection::connected && isset) { + //There's stuff to read from connections[co]->tcpsock + if(nc->packet_based) { + while(nc->checktcp()) { + Net_buf nb; + nc->receivetcp(&nb); + packetreceived(&nb, true); + } + } + else { + //Not packet based, read everything into incoming + Byte buf[1024]; + int num; + while((num=nc->receivetcp(buf, 1024))) { + nc->sendtcp(buf, num); //auto-echo + Buf *out=nc->incoming; + //Do something quick about backspace + Byte *p1=buf; + while(num) { + if(*p1==8) { + //Found a backspace, remove if not at line start + Dword outsize=out->size(); + if(outsize) { + Byte last=*(out->get()+outsize-1); + if(last!='\r' && last!='\n') + out->resize(outsize-1); + } + } + else { + out->append(p1, 1); + } + p1++; + num--; + } + } + } + } + } + verify_connections(); //Verify all client connections and notify if something changed + } + verify_connections(); //One last time, just to be sure :) + for(i=0; icommit(); + if(client_connection) + client_connection->commit(); +} + +void Net::verify_connections() { + bool change=false; + int i; + for(i=0; istate()==Net_connection::disconnected) { + net_param->client_deconnect(nc); + delete nc; + connections.remove(i); + i--; + change=true; + } + } + if(change) + notify_all(); +} + +void Net::verify_server_connection() { + if(client_connection->state()!=Net_connection::connected) { + //Graceful and sudden server deconnection + stop_client(); + net_param->server_deconnect(); + } +} + +void Net::addwatch(Word id, Net_callable *nc) { + for(int i=0; iid == id && callbacks[i]->net_callable == nc) + return; + Net_receive_cb *cb = new Net_receive_cb(id, nc); + callbacks.add(cb); +} + +void Net::removewatch(Word id, Net_callable *nc) { + for(int i=0; iid == id && callbacks[i]->net_callable == nc) { + delete callbacks[i]; + callbacks.remove(i); + break; + } +} + +void Net::sendudp(Dword to, Packet *p) { + Net_buf nb; + p->write(&nb); + + udpsin.sin_addr.s_addr = htonl(to); + int i; + for(i=0; istate()==Net_connection::connected) + return client_connection; + else + return NULL; +} + +void Net::start_server(bool sock) { + if(server_connection) + return; + skelton_msgbox("Server listening for TCP connection on port %i... ", net_param->tcpport()); + if(active && sock) { + Net_connection_tcp *sc=new Net_connection_tcp(net_param->tcpport()); + if(sc->state()==Net_connection::invalid) { + skelton_msgbox("but it failed with [%s]\n", last_error); + delete sc; + } + else { + skelton_msgbox("Ok\n"); + sockaddr_in sin; + addr_size_t len=sizeof(sin); + callwsa(getsockname(sc->getFD(), (sockaddr *) &sin, &len)); + sc->from=ntohl(sin.sin_addr.s_addr); + msgbox("server_connection: %p\n", sc); + server_connection=sc; + return; + } + } + //Fail-safe: internal loopback connection + server_connection=new Net_connection(); +} + +void Net::stop_server() { + connections.deleteall(); + suspend_server(); + notify_all(); +} + +void Net::suspend_server() { + if(server_connection) { + skelton_msgbox("Server stop listening\n"); + delete server_connection; + server_connection=NULL; + } +} + +void Net::sendtcp(Net_connection *nc, Packet *p) { + if(nc && p) { + nc->sendtcp(p); + } +} + +void Net::dispatch(Packet *p, Dword pt, Net_connection *nc) { + if(!p) + return; + p->packet_id=pt; + for(int i=0; ipacket_based && net_param->is_dispatchable(nc2, p)) { + connections[i]->sendtcp(p); + } + } +} + +Net_connection *Net::start_loopback_client() { + if(client_connection) + return NULL; + client_connection=new Net_connection(); + Net_connection *accepted_connection=new Net_connection(); + connections.add(accepted_connection); + client_connection->connect(accepted_connection); + return accepted_connection; +} + +void Net::start_client(Dword adr, int port) { + if(client_connection) + return; + if(!active) { + msgbox("Net::start_client: can't start client when not active!\n"); + return; + } + client_connection=new Net_connection_tcp(0); + if(client_connection->state()==Net_connection::invalid) { + delete client_connection; + client_connection=NULL; + return; + } + if(!port) + port = net_param->tcpport(); + client_connection->connect(adr, port); + msgbox("client_connection: %p\n", client_connection); +} + +void Net::stop_client() { + if(client_connection) { + client_connection->commit(); + delete client_connection; + client_connection=NULL; + } +} + +Net_connection_tcp *Net::start_other(Dword adr, int port) { + Net_connection_tcp *nc=new Net_connection_tcp(0, false); + if(nc->state()==Net_connection::invalid) { + delete nc; + return NULL; + } + nc->connect(adr, port); + return nc; +} + +Net_connection_tcp *Net::start_other(const char *host, int port) { + Net_connection_tcp *nc=new Net_connection_tcp(0, false); + if(nc->state()==Net_connection::invalid) { + delete nc; + return NULL; + } + nc->connect(host, port); + return nc; +} + +Packet *Net::net_buf2packet(Net_buf *nb, bool tcp) { + Packet p; + if(!p.read(nb)) { + //Invalid signature + return NULL; + } + Packet *packet=net_param->alloc_packet(p.packet_id); + packet->istcp=tcp; + if(packet) { + if(!packet->read(nb)) { + delete packet; + packet=NULL; + skelton_msgbox(" bad packet\n "); + Word size=min(nb->len(), 128); + nb->reset(); + int i; + for(i=0; ibuf[i]); + } + } + else { + skelton_msgbox("Packet_id %i not allocated by alloc_packet()!\n", p.packet_id); + } + return packet; +} + +bool Net::connected() { + if(!client_connection || client_connection->state()!=Net_connection::connected) + return false; + return true; +} + +void Net::sendtcp(Packet *p) { + if(client_connection) { + client_connection->sendtcp(p); + verify_server_connection(); // verify if server has been disconnected + } +} + +Dword Net::dotted2addr(const char *host) { + Dword lAddr = INADDR_NONE; + + // check that we have a string + if(*host) { + // check for a dotted-IP address string + lAddr = ntohl(inet_addr(host)); + } + return lAddr; +} + +Dword Net::getaddress(const char *host) { + Dword lAddr = INADDR_ANY; + + // check that we have a string + if(*host) { + char tube[1024]; + strncpy(tube, host, 1023); + tube[1023] = 0; + char *adr = strchr(tube, ':'); + if(adr) { + port_resolve = atoi(adr+1); + *adr = 0; + } else { + port_resolve = 0; + } + // check for a dotted-IP address string + lAddr = ntohl(inet_addr(tube)); + + // If not an address, then try to resolve it as a hostname + if((lAddr == INADDR_NONE) && (strcmp(tube, "255.255.255.255"))) { + lAddr = 0; + #ifdef UGS_LINUX + struct hostent *lpstHost; + // Blocking call!! + lpstHost = gethostbyname(tube); + if(lpstHost) { // success + lAddr = ntohl(*(Dword*)lpstHost->h_addr_list[0]); + } + #endif + // devrait etre remplacer par ceci (async): + #ifdef UGS_DIRECTX + gethostbyname_cancel(); + name_handle = WSAAsyncGetHostByName(hwnd, WM_USER, tube, name_buf, MAXGETHOSTSTRUCT); + if(name_handle == 0) {// si erreur retourne 0 + name_resolve = 0; // impossible de résoudre DNS à cause de Winsock et/ou Doze + } + #endif + } + } + return lAddr; +} + +void Net::stringaddress(char *st, Dword adr) { + struct in_addr in; + in.s_addr = htonl(adr); + strcpy(st, inet_ntoa(in)); +} + +void Net::stringaddress(char *st, Dword adr, int port) { + struct in_addr in; + in.s_addr = htonl(adr); + strcpy(st, inet_ntoa(in)); + char st2[16]; + sprintf(st2, ":%i", port); + strcat(st, st2); +} + +void Net::gethostbyname_completed(bool success) { +#ifdef UGS_DIRECTX + name_handle = 0; + if(success) + name_resolve = ntohl(*(Dword *)((LPHOSTENT) name_buf)->h_addr); +#endif + if(!success) + name_resolve = 0; +} + +void Net::gethostbyname_cancel() { +#ifdef UGS_DIRECTX + if(name_handle) + WSACancelAsyncRequest(name_handle); + name_handle = 0; + name_resolve = -1; +#endif +} + +void Net::packetreceived(Net_buf *nb, bool tcp) { + Packet pac; + struct in_addr in; + if(!pac.read(nb)) { + //Invalid signature + return; + } + in.s_addr = htonl(pac.from_addr); + for(int i=0; iid == pac.packet_id) { + Packet *packet=net_buf2packet(nb, tcp); + if(packet) { + callbacks[i]->net_callable->net_call(packet); + } else { + skelton_msgbox("Packet_id %i not allocated by net_buf2packet()!\n", pac.packet_id); + } + } +} + +void Net::receiveudp(int sock, Net_buf *p) { + sockaddr_in tsin; + addr_size_t tsin_size = sizeof(tsin); + int temp = recvfrom(sock, (char *) p->buf, 1024, 0, (sockaddr *) &tsin, &tsin_size); + p->from = NULL; + p->from_addr = ntohl(tsin.sin_addr.s_addr); + if(getlasterror(temp)) { + char *msg = net->failed(); + if(msg) { + skelton_msgbox("Net::receiveudp: Error. Ignoring it. [%s]\n", msg); + } + return; + } + /* + skelton_msgbox("Recv %i bytes from %s (UDP %i): ", p->size, inet_ntoa(tsin.sin_addr), sock); + for(int i=0; isize; i++) + skelton_msgbox("%i,", p->buf[i]); + skelton_msgbox("\n"); + */ +} + +bool Net::accept() { + if(!active) + return false; + sockaddr_in bob; + addr_size_t boblen=sizeof(bob); + + if(!server_connection || server_connection->state()==Net_connection::invalid) + return false; + int sock=::accept(server_connection->getFD(), (sockaddr *) &bob, &boblen); + if(sock > 0) { + Dword adr=ntohl(bob.sin_addr.s_addr); + Word port=ntohs(bob.sin_port); + skelton_msgbox("Net::accept adding connection from %s:%i\n", inet_ntoa(bob.sin_addr), port); + Net_connection_tcp *nc=new Net_connection_tcp(sock, adr, port); + if(net_param->accept_connection(nc)) { + connections.add(nc); + } + else { + skelton_msgbox("...fail because too many connections or connection refused\n"); + delete nc; + return false; + } + net_param->client_connect(nc); + notify_all(); + return true; + } + return false; +} + +int Net::checkreceive(int s) { + fd_set fdsoc; + FD_ZERO(&fdsoc); + FD_SET(s, &fdsoc); + + timeval empty_tv; + empty_tv.tv_sec = 0; + empty_tv.tv_usec = 0; + + int tube = select(s+1, &fdsoc, NULL, NULL, &empty_tv); + getlasterror(tube); + char *msg = failed(); + if(msg) { + skelton_msgbox("Net::checkreceive error [%s], ignoring it\n", msg); + } + return tube; +} + +bool Net::checkerror(int quel) { + if(quel == 0) + return false; +#ifdef UGS_DIRECTX + switch(quel) { +// case WSAEWOULDBLOCK: last_error = "The socket is marked as non-blocking and this operation would block."; break; + case WSAEWOULDBLOCK: skelton_msgbox("Net::checkerror: WSAEWOULDBLOCK ignored.\n"); return false; // special: pas vraiment une erreur + case WSASYSNOTREADY: last_error = "The underlying network subsystem is not ready for network communication."; break; + case WSAVERNOTSUPPORTED: last_error = "The version of Winsock API support requested is not provided by this implementation."; break; + case WSAEINVAL: last_error = "Operation invalid or not supported by this Windows Sockets DLL"; break; + case WSANOTINITIALISED: last_error = "A successful WSAStartup() must occur before using this API."; break; + case WSAENETDOWN: last_error = "The Windows Sockets implementation has detected that the network subsystem has failed."; break; + case WSAEADDRINUSE: last_error = "The specified address is already in use."; break; + case WSAEINTR: last_error = "The (blocking) call was canceled via WSACancelBlockingCall()"; break; + case WSAEINPROGRESS: last_error = "A blocking Windows Sockets call is in progress."; break; + case WSAEAFNOSUPPORT: last_error = "The specified address family is not supported by this protocol."; break; + case WSAENOBUFS: last_error = "Not enough buffers available, too many connections."; break; + case WSAENOTSOCK: last_error = "The descriptor is not a socket."; break; + case WSAHOST_NOT_FOUND: last_error = "Authoritative Answer Host not found."; break; + case WSATRY_AGAIN: last_error = "Non-Authoritative Host not found, or SERVERFAIL."; break; + case WSANO_RECOVERY: last_error = "Non recoverable errors, FORMERR, REFUSED, NOTIMP."; break; + case WSANO_DATA: last_error = "Valid name, no data record of requested type."; break; + case WSAEACCES: last_error = "The requested address is a broadcast address."; break; + case WSAEFAULT: last_error = "Invalid or too small argument"; break; + case WSAENETRESET: last_error = "The connection must be reset because Winsock implementation dropped it."; break; + case WSAENOTCONN: last_error = "The socket is not connected (SOCK_STREAM only)."; break; + case WSAEOPNOTSUPP: last_error = "MSG_OOB was specified, but the socket is not of type SOCK_STREAM."; break; + case WSAESHUTDOWN: last_error = "The socket has been shutdown."; break; + case WSAEMSGSIZE: last_error = "The datagram is larger than the maximum supported by Winsock."; break; + case WSAECONNABORTED: last_error = "The virtual circuit was aborted due to timeout or other failure."; break; + case WSAECONNRESET: last_error = "The virtual circuit was reset by the remote side."; break; + case WSAEADDRNOTAVAIL: last_error = "The specified address is not available from the local machine."; break; + case WSAEDESTADDRREQ: last_error = "A destination address is required."; break; + case WSAENETUNREACH: last_error = "The network can't be reached from this host at this time."; break; + case WSAECONNREFUSED: last_error = "The attempt to connect was forcefully rejected."; break; + case WSAEISCONN: last_error = "The socket is already connected."; break; + case WSAEMFILE: last_error = "No more file descriptors are available."; break; + case WSAETIMEDOUT: last_error = "Attempt to connect timed out without establishing a connection."; break; + case WSAEHOSTUNREACH: last_error = "The destination address is unreachable."; break; + default: last_error = "Unknown error"; break; + } +#endif +#ifdef UGS_LINUX + last_error = strerror(errno); + switch(errno) { + case EINPROGRESS: + return false; + case EHOSTUNREACH: + return false; + } + skelton_msgbox("errno = %i (%s)\n", errno, last_error); +#endif + return true; +} + +bool Net::getlasterror(int quel) { + if(quel >= 0) + return false; + #if defined(UGS_DIRECTX) + return checkerror(WSAGetLastError()); + #elif defined(UGS_LINUX) + return checkerror(errno); + #else + #error "What the heck is the target???" + #endif +} + +void Net::callwsa(int quel) { + if(getlasterror(quel)) + new Error(last_error); +} + +char *Net::failed() { + char *tube; + if(last_error) { + tube = last_error; + last_error = NULL; + } else + tube = NULL; + return tube; +} + +Net *net=NULL; diff --git a/skelton/common/notify.cpp b/skelton/common/notify.cpp new file mode 100644 index 0000000..4549b77 --- /dev/null +++ b/skelton/common/notify.cpp @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "error.h" +#include "notify.h" + +void Observable::add_watch(Notifyable *n) { + notes.add(n); +} + +void Observable::remove_watch(Notifyable *n) { + notes.remove_item(n); +} + +void Observable::notify_all() { + for(int i=0; inotify(); + } +} + +Observable::~Observable() { + if(notes.size()) + skelton_msgbox("Observable %p was destroyed while still watched (%i watchers)\n", this, notes.size()); +} diff --git a/skelton/common/overmind.cpp b/skelton/common/overmind.cpp new file mode 100644 index 0000000..ada1bc6 --- /dev/null +++ b/skelton/common/overmind.cpp @@ -0,0 +1,176 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "overmind.h" + +Overmind overmind; +Inter* ecran = NULL; + +Overmind::Overmind() { + framecount = 0; + paused = false; + done = false; +} + +Overmind::~Overmind() { + clean_up(); +} + +void Overmind::clean_up() { + while(execs.size()) { + Executor *e = execs.last(); + if(e && e->self_destruct) + delete e; + execs.removelast(); + } +} + +void Overmind::pause() { + paused = true; +} + +void Overmind::unpause() { + paused = false; +} + +void Overmind::step() { + if(paused) + return; + framecount++; + for(int i=0; istep(); + if(e->done) { + if(e->self_destruct) + delete e; + execs.remove(i); + i--; + } + } + } + if(execs.size() == 0) + done = true; +} + +void Overmind::start(Executor* e) { + execs.add(e); + done = false; +} + +void Overmind::stop(Executor* e) { + for(int i=0; iself_destruct) + delete e; + execs.replace(i, NULL); + } + } +} + +Executor::Executor(bool self_des) { + done = paused = false; + self_destruct = self_des; +} + +Executor::~Executor() { + while(modules.size()) + remove(); +} + +void Executor::remove() { + delete modules.last(); + modules.removelast(); +} + +void Executor::step() { + if(paused) + return; + if(modules.size()) { + if(!modules.last()->done) { + if(modules.last()->first_time) { + modules.last()->first_time = false; + modules.last()->init(); + } else { + modules.last()->step(); + } + } + while(modules.size() && modules.last()->done) { + remove(); + } + } + if(!modules.size()) + done=true; +} + +void Executor::add(Module* m) { + modules.add(m); + m->parent=this; +} + +Module::Module(): done(false) { + first_time = true; +} + +Module::~Module() { +} + +void Module::step() { +} + +void Module::init() { +} + +void Module::exec(Module* module) { + call(module); + ret(); +} + +void Module::call(Module* module) { + parent->add(module); +} + +void Module::ret() { + done=true; +} + +Module_thread::Module_thread() { + parent = new Executor(true); + parent->add(this); + overmind.start(parent); +} + +Menu::Menu(Inter* base) { + if(base) + inter=new Inter(base); + else + inter=new Inter(); + result=NULL; + old_ecran=ecran; +} + +Menu::~Menu() { + delete inter; + ecran=old_ecran; + if(ecran) + ecran->dirt_all(); +} + +void Menu::init() { + old_ecran=ecran; + ecran = inter; +} + +void Menu::step() { + ecran = inter; + if(ecran) { + result = ecran->do_frame(); + } else + result = NULL; +} diff --git a/skelton/common/packet.cpp b/skelton/common/packet.cpp new file mode 100644 index 0000000..d926725 --- /dev/null +++ b/skelton/common/packet.cpp @@ -0,0 +1,118 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "error.h" +#include "net.h" +#include "net_buf.h" +#include "packet.h" + +Packet::Packet() { + istcp=false; + from=NULL; + from_addr=0; + packet_id=0xFF; +} + +void Packet::write(Net_buf *p) { + p->reset(); + p->write_byte((Byte) packet_id); +} + +bool Packet::read(Net_buf *p) { + p->reset(); + from = p->from; + from_addr = p->from_addr; + packet_id = p->read_byte(); + return true; +} + +Packet_tcp::Packet_tcp() { + istcp=true; +} + +bool Packet_tcp::read(Net_buf *p) { + if(!Packet::read(p)) + return false; + if(!istcp) + return false; + return true; +} + +Packet_udp::Packet_udp() { + istcp=false; + magic=net->net_param->magic(); +} + +void Packet_udp::write(Net_buf *p) { + Packet::write(p); + p->write_dword(magic); +} + +bool Packet_udp::read(Net_buf *p) { + if(!Packet::read(p)) + return false; + magic = p->read_dword(); + if(magic!=net->net_param->magic()) + return false; + if(istcp) + return false; + return true; +} + +void Packet_ping::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_dword(uid); +} + +bool Packet_ping::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + uid = p->read_dword(); + return true; +} + +void Packet_ping::answer(Packet_ping *p2) { + uid=p2->uid; + net->sendtcp(p2->from, this); +} + +Dword Exec_ping::next_uid=0; + +Exec_ping::Exec_ping(Packet_ping *p, Word pt, Net_callable *netc, Net_connection *d) { + dest = d; + type = pt; + uid = p->uid = next_uid++; + net_callable = netc; + if(net_callable) + net->addwatch(type, this); + if(d) { + net->sendtcp(d, p); + } + else { + net->sendtcp(p); + } +} + +Exec_ping::~Exec_ping() { + if(net_callable) + net->removewatch(type, this); +} + +void Exec_ping::net_call(Packet *p2) { + Packet_ping *p=(Packet_ping *) p2; + if(p && validate(p) && net_callable) { + net_callable->net_call(p); + net_callable=NULL; + net->removewatch(type, this); + } + delete p2; +} + +bool Exec_ping::validate(Packet_ping *p) { + if(dest && p->from!=dest) + return false; + return p->packet_id == type && p->uid == uid; +} diff --git a/skelton/common/pcx.cpp b/skelton/common/pcx.cpp new file mode 100644 index 0000000..c63ae92 --- /dev/null +++ b/skelton/common/pcx.cpp @@ -0,0 +1,77 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "pcx.h" + +Pcx::Pcx(Res& res) { + Error *error; + res.read(&h, sizeof(h)); + if(h.nplane != 1) + error = new Error("This PCX file has more than 1 plane!"); + if(h.version != 5) + error = new Error("PCX file must be version 5"); + width_ = h.x2-h.x1+1; + height_ = h.y2-h.y1+1; + pic_=new Byte[width_*height_]; + if(pic_==NULL) + error = new Error("Not enough memory to load PCX"); + pal_=new Byte[256*3]; + if(pal_==NULL) + error = new Error("Not enough memory to load PCX"); + Byte* buf = (Byte *) res.buf(); + Byte c, num; + Byte* out = pic_; + int x,i; + if(width_ == h.byteperline) { // depack plus rapidement si bonne largeur + for(i=0; i < height_; i++) { + x = 0; + while(x < width_) { + c = *buf++; + if(0xC0 == (0xC0 & c)) { + num = (Byte) (0x3F & c); + c = *buf++; + x += num; + while(num--) + *out++ = c; + } else { + *out++ = c; + x++; + } + } + } + } else { // maudit PCX de merde avec des mauvais bytes au bout des lignes!! + for(i=0; i < height_; i++) { + x = 0; + while(x < h.byteperline) { + c = *buf++; + if(0xC0 == (0xC0 & c)) { + num = (Byte) (0x3F & c); + c = *buf++; + x += num; + if(x > width_) { + num = (Byte) (num - (x-width_)); + } + while(num--) + *out++ = c; + } else { + if(x < width_) + *out++ = c; + x++; + } + } + } + } + if(*buf++ != 12) + error = new Error("Can't find palette in PCX file"); + cpy(pal_, buf, 768); +} + +Pcx::~Pcx() { + if(pic_) + delete[] pic_; + if(pal_) + delete[] pal_; +} diff --git a/skelton/common/pcx24.cpp b/skelton/common/pcx24.cpp new file mode 100644 index 0000000..8c92c1b --- /dev/null +++ b/skelton/common/pcx24.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "res.h" +#include "pcx24.h" + +Pcx24::Pcx24(Res& res) { + Error *error; + res.read(&h, sizeof(h)); + if(h.nplane != 3) + error = new Error("PCX24 need a 3 plane true-color file!"); + if(h.version != 5) + error = new Error("PCX file must be version 5"); + width_ = h.x2-h.x1+1; + height_ = h.y2-h.y1+1; + pic_=new Byte[width_*height_*3]; + Byte *temp=new Byte[h.byteperline*height_*3]; + if(pic_==NULL || temp==NULL) + error = new Error("Not enough memory to load PCX"); + Byte* buf = (Byte *) res.buf(); + Byte c, num; + int x,i; + int total_byte = h.byteperline*3; + for(i=0; i < height_; i++) { + x = 0; + Byte *out = temp + i*total_byte; + while(x < total_byte) { + c = *buf++; + if(0xC0 == (0xC0 & c)) { + num = (Byte) (0x3F & c); + c = *buf++; + x += num; + while(num--) + *out++ = c; + } else { + *out++ = c; + x++; + } + } + } + for(i=0; i < height_; i++) { + for(int plane=0; plane<3; plane++) { + Byte *out = pic_ + i*width_*3 + plane; + Byte *in = temp + (i*3+plane)*h.byteperline; + for(x=0; x < width_; x++) { + *out = *in++; + out += 3; + } + } + } + delete[] temp; +} + +Pcx24::~Pcx24() { + if(pic_) + delete[] pic_; +} diff --git a/skelton/common/random.cpp b/skelton/common/random.cpp new file mode 100644 index 0000000..16f1e27 --- /dev/null +++ b/skelton/common/random.cpp @@ -0,0 +1,43 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "types.h" +#include "random.h" + +Random ugs_random; + +Random::Random() { + set_seed(time(NULL)); +} + +Random::Random(int p) { + set_seed(p); +} + +Word Random::rnd(int and) { // better proc + int tmp; + seed = seed * 0x41c64e6d + 0x00003039; + tmp = seed >> 10; + + return (Word) (tmp & and); +}; + +Word Random::crap_rnd(int and) { // crappy proc + int tmp; + tmp = seed * 0x41c64e6d + 0x00003039; + seed = tmp >> 10; + + return (Word) (tmp & and); +}; + +int Random::get_seed() const { + return seed; +} + +void Random::set_seed(int p) { + seed = p; +} diff --git a/skelton/common/raw.cpp b/skelton/common/raw.cpp new file mode 100644 index 0000000..56aecaa --- /dev/null +++ b/skelton/common/raw.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include "error.h" +#include "res.h" +#include "raw.h" + +char Raw::Head::signature[]={0x6d, 0x68, 0x77, 0x61, 0x6e, 0x68}; + +#define swap(A) ((Word) (((((Word)A)>>8) )| ((((Word)A) )<<8))) +void Raw::Head::xlat() { + version=swap(version); + width=swap(width); + height=swap(height); + palettesize=swap(palettesize); + HDPI=swap(HDPI); + VDPI=swap(VDPI); + gamma=swap(gamma); +} +#undef swap + +Raw::Raw(Res& res) { + Error *error; + res.read(&h, sizeof(h)); + h.xlat(); + if(strncmp(h.sig, Head::signature, sizeof(h.sig))) + error = new Error("Invalid raw image file"); + if(h.palettesize<1) { + pal_=NULL; + pic_=new Byte[h.width*h.height*2]; + Byte *temp=new Byte[h.width*h.height*3]; + if(pic_==NULL || temp==NULL) + error = new Error("Not enough memory to load image"); + res.read(temp, h.width*h.height*3); + Byte r,g,b; + for(int y=0; y>3); + g = (Byte) (temp[(x+y*h.width)*3+1]>>2); + r = (Byte) (temp[(x+y*h.width)*3+2]>>3); + *(((Word *)pic_)+(x+y*h.width)) = (Word) (r+(g<<5)+(b<<11)); + } + delete(temp); + } else { + pal_=new Byte[h.palettesize*3]; + if(pal_==NULL) + error = new Error("Not enough memory to load image"); + res.read(pal_, h.palettesize*3); + pic_=new Byte[h.width*h.height]; + if(pic_==NULL) + error = new Error("Not enough memory to load image"); + res.read(pic_, h.width*h.height); + } +} + +Raw::Raw(int w, int h2, int ps) { + memcpy(h.sig, Head::signature, sizeof(Head::signature)); + h.width = w; + h.height = h2; + h.palettesize = ps; + h.version = 4; + h.HDPI = h.VDPI = 300; + h.gamma = 0; + memset(h.reserved, 0, sizeof(h.reserved)); + pal_=NULL; + pic_=NULL; +} + +Raw::~Raw() { + if(pic_) + delete pic_; + if(pal_) + delete pal_; +} + +void Raw::write(Res_dos& res) { + h.xlat(); + res.write(&h, sizeof(h)); +} diff --git a/skelton/common/res_compress.cpp b/skelton/common/res_compress.cpp new file mode 100644 index 0000000..eeab115 --- /dev/null +++ b/skelton/common/res_compress.cpp @@ -0,0 +1,118 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "zlib.h" +#undef FAR +#include "res_compress.h" + +Res_compress::Res_compress(const char *fil, Res_mode pmode, bool res_doze) { + mode = pmode; + ressize = 0; + write_pos = 0; + if(res_doze) + res = new Res_doze(fil); + else { + res_dos = new Res_dos(fil, mode); + res = res_dos; + exist = res_dos->exist; + } + if((mode == RES_TRY && exist) || mode == RES_READ) { + read_uncompress(); + } +} + +Res_compress::~Res_compress() { + if(mode == RES_WRITE || mode == RES_CREATE) { + if(res) + write_compress(NULL); + } + if(res) + delete res; + if(_buf) + free(_buf); +} + +void Res_compress::read_uncompress() { + exist = false; + Byte *temp = (Byte *) res->buf(); // lit le fichier en entier dans '_buf' + ressize = *(Dword *) temp; + Byte *source = temp + 4; + int src_size = res->size() - 4; + skelton_msgbox("Res_compress::Res_compress: Reading compressed file original size = %i, compressed = %i\n", ressize, src_size); + if(ressize < 0 || src_size < 0) { + if(mode == RES_TRY) + return; + else + (void) new Error("Unable to uncompress file.\n"); + } + _buf = (Byte *) malloc(ressize); + if(_buf == NULL) { + if(mode == RES_TRY) + return; + else + (void) new Error("Out of memory allocating buffer (%i bytes)\n",ressize); + } + + unsigned long dest_len = ressize; + int error = uncompress(_buf, &dest_len, source, src_size); + if(error != Z_OK) { + if(mode == RES_TRY) + return; + else + (void) new Error("Unable to uncompress file, error #%i", error); + } + if(dest_len != ressize) { + if(mode == RES_TRY) + return; + else + (void) new Error("Error uncompressing, destination len should be:%i but returned:%i",ressize,dest_len); + } + exist = true; +} + +void Res_compress::write(const void *b, int nb) { + if(write_pos + nb > ressize) { + ressize = ressize + max(nb, 16384); + _buf = (Byte *) realloc(_buf, ressize); + if(_buf == NULL) + (void) new Error("Unable to reallocate buffer (need %i bytes)\n", ressize); + } + memcpy(_buf + write_pos, b, nb); + write_pos += nb; +} + +Dword Res_compress::size() { + return ressize; +} + +Byte *Res_compress::write_compress(Dword *size) { + if(!res) + (void)new Error("Trying to write_compress a second time!"); + if(!res_dos) + (void)new Error("Trying to write_compress a Res_doze!"); + if(!_buf) + return NULL; + unsigned long dest_len = write_pos + 65540; + Byte *temp = (Byte *) malloc(dest_len); + *((Dword *)temp)=write_pos; + int error = compress(temp+4, &dest_len, _buf, write_pos); + if(error != Z_OK) { + (void) new Error("Unable to compress file, error #%i", error); + } + skelton_msgbox("Res_compress::write_compress: writing file, original was %i, packed = %i\n", write_pos, dest_len); + res_dos->write(temp, dest_len+4); + delete res_dos; + res_dos=NULL; + res=NULL; + if(size) { + *size=dest_len+4; + } + else { + free(temp); + temp=NULL; + } + return temp; +} diff --git a/skelton/common/resfile.cpp b/skelton/common/resfile.cpp new file mode 100644 index 0000000..bcb2f24 --- /dev/null +++ b/skelton/common/resfile.cpp @@ -0,0 +1,130 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include "error.h" +#include "res.h" +#include "resfile.h" + +#define stricmp strcasecmp + +Resdata::Resdata(char *resname, int ressize, Byte *resdata, Resdata *list) { + name = resname; + size = ressize; + data = resdata; + next = list; +} + +Resdata::~Resdata() { + delete name; + delete data; + if(next) + delete next; +} + +Resfile::Resfile(const char *fname, bool ro) { + list = NULL; + + if(ro) + res = new Res_dos(fname, RES_TRY); + else + res = new Res_dos(fname, RES_CREATE); + + if(res->exist) { + skelton_msgbox("Resfile %s is fine\n", fname); + thaw(); + } else + skelton_msgbox("Resfile %s does not exist\n", fname); +} + +Resfile::~Resfile() { + clear(); + if(res) + delete res; +} + +void Resfile::thaw() { + char sig[sizeof(signature)]; + int resnamelen; + char *resname; + Byte *resdata; + int ressize; + + res->position(0); + + res->read(sig, sizeof(signature)); + if(strncmp(sig, signature, sizeof(signature)) != 0) { + msgbox("Resfile::thaw(): invalid signature\n"); + return; + } + + do { + res->read(&resnamelen, sizeof(resnamelen)); + if(resnamelen == 0) + break; + resname = new char[resnamelen]; + res->read(resname, resnamelen); + res->read(&ressize, sizeof(ressize)); + resdata = new Byte[ressize]; + res->read(resdata, ressize); + + list = new Resdata(resname, ressize, resdata, list); + } while(resnamelen > 0); + +} + +void Resfile::clear() { + if(list) + delete list; + + list = NULL; +} + +int Resfile::get(const char *resname, Byte **resdata) { + Resdata *ptr; + + ptr = list; + + while(ptr != NULL) { + if(stricmp(ptr->name, resname) == 0) + break; + ptr = ptr->next; + } + + if(ptr) { + *resdata = ptr->data; + return ptr->size; + } else { + *resdata = NULL; + return 0; + } +} + +void Resfile::remove(const char* resname) { + Resdata *ptr, *prev; + + prev = NULL; + ptr = list; + + while(ptr != NULL) { + if(stricmp(ptr->name, resname) == 0) + break; + prev = ptr; + ptr = ptr->next; + } + + if(ptr) { + if(prev) + prev->next = ptr->next; + else + list = ptr->next; + ptr->next = NULL; + delete ptr; + } + //Can somebody tell me why Resfile::list isn't an Array? + // We all know linked lists suck, don't we? Whatever... +} diff --git a/skelton/common/resmanager.cpp b/skelton/common/resmanager.cpp new file mode 100644 index 0000000..9103583 --- /dev/null +++ b/skelton/common/resmanager.cpp @@ -0,0 +1,40 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "resfile.h" +#include "resmanager.h" + +Resmanager::Resmanager() { +} + +void Resmanager::loadresfile(const char *fname) { + Resfile* rf=new Resfile(fname); + files.add(rf); + Resdata* rd = rf->list; + while(rd) { + char *name=rd->name; + int i; + for(i=files.size()-2; i>=0; i--) + files[i]->remove(name); + rd = rd->next; + } +} + +int Resmanager::get(const char *resname, Byte **resdata) { + int i=files.size(); + while(i--) { + int ret=files[i]->get(resname, resdata); + if(*resdata) + return ret; + } + return 0; +} + +Resmanager::~Resmanager() { + files.deleteall(); +} + +Resmanager *resmanager; diff --git a/skelton/common/reswriter.cpp b/skelton/common/reswriter.cpp new file mode 100644 index 0000000..21944fb --- /dev/null +++ b/skelton/common/reswriter.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "res.h" +#include "resfile.h" + +void Resfile::freeze() { + int resnamelen; + Resdata *ptr; + + res->write(&signature, sizeof(signature)); + + ptr = list; + + while(ptr != NULL) { + resnamelen = strlen(ptr->name)+1; + res->write(&resnamelen, sizeof(resnamelen)); + res->write(ptr->name, resnamelen); + res->write(&ptr->size, sizeof(ptr->size)); + res->write(ptr->data, ptr->size); + ptr = ptr->next; + } + + resnamelen = 0; + res->write(&resnamelen, sizeof(resnamelen)); +} + +void Resfile::add(const char *resname, const int size, const char *resdata) { + char *myname; + Byte *mydata; + + myname = new char[strlen(resname)+1]; + memcpy(myname, resname, strlen(resname)+1); + mydata = new Byte[size]; + memcpy(mydata, resdata, size); + + list = new Resdata(myname, size, mydata, list); +} diff --git a/skelton/common/sprite.cpp b/skelton/common/sprite.cpp new file mode 100644 index 0000000..badfbb3 --- /dev/null +++ b/skelton/common/sprite.cpp @@ -0,0 +1,262 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "types.h" +#include "bitmap.h" +#include "video.h" +#include "sprite.h" + +#define FONT_SIZE (141-32) + +void Sprite::set_hotspot(const int hx, const int hy) { + if(hx == CENTER) + hot_x = width>>1; + else + if(hx == CORNER) + hot_x = width-1; + else + hot_x = hx; + if(hy == CENTER) + hot_y = height>>1; + else + if(hy == CORNER) + hot_y = height-1; + else + hot_y = hy; +} + +Sprite::Sprite(const Bitmap& b, const int hx, const int hy, const bool dx): + Bitmap(b[0], b.width, b.height, b.realwidth, COPY) +{ + set_hotspot(hx, hy); + if(dx) + create_directx_surface(); +} + +void Sprite::draw(const Bitmap& d, const int dx, const int dy) const { + int tx, ty; + tx = dx-hot_x; + ty = dy-hot_y; + if(d.clip(tx, ty, this)) + return; + for(int y=clip_y1; y<=clip_y2; y++) { + for(int i=clip_x1; i<=clip_x2; i++) { + Byte pel = *(operator[](y-ty)+(i-tx)); + // optimisation etant donne que le mask est toujours == 0 + // a cause de SVGALIB + if(pel) + d.fast_pel(i, y, pel); + } + } +} + +void Sprite::draw(const Video_bitmap* d, const int dx, const int dy) const { + d->put_sprite(*this, dx, dy); +} + +/*void Sprite::color_draw(const Remap& remap, const Bitmap& d, int dx, int dy) const { + int x1, y1, x2, y2; + dx -= hot_x; + dy -= hot_y; + x1=max(0, dx); + x2=min(d.width-1, dx+width-1); + y1=max(0, dy); + y2=min(d.height-1, dy+height-1); + if(x1>=d.width || x2<0 || y1>=d.height || y2<0) + return; + for(int y(y1); y<=y2; y++) + for(int i=x1; i<=x2; i++) { + Byte pel = *(operator[](y-dy)+(i-dx)); + if(pel != mask) + d.fast_pel(i, y, remap.map[pel]); + } +}*/ + +Fontdata::Fontdata(Res &res, int s) { + Bitmap *tmp; + shrink = s; + int w, h, rw; + for(int i=0; iwidth - shrink, 3); + delete tmp; + } else { + spr[i] = NULL; + pre_width[i] = 0; + } + } +} + +Fontdata::Fontdata(const Fontdata &o) { + for(int i=0; ihot_x, o.spr[i]->hot_y); + else + spr[i] = NULL; + pre_width[i] = o.pre_width[i]; + } + shrink = o.shrink; +} + +Fontdata::~Fontdata() { + for(int i=0; i 32 && c < 127) + return c-33; // valeur standard + switch(c) { + // ascii 183 suivit d'un chiffre pour faire un glyph (exemple: ·2) + case '·': + ret=(int) (*(*m)++)-48 + 133; + if(ret<133 || ret>137) + return -2; + break; + case 'Ç': ret='C'; break; + case 'ù': ret=139; break; + case 'û': ret=140; break; + case 'ü': ret='u'; break; + case 'é': ret=127; break; + case 'è': ret=128; break; + case 'ê': ret=129; break; + case 'ë': ret=130; break; + case 'à': ret=131; break; + case 'â': + case 'ä': ret='a'; break; + case 'ç': ret=132; break; + case 'Ù': + case 'Û': + case 'Ü': ret='U'; break; + case 'À': + case 'Â': + case 'Ä': ret='A'; break; + case 'É': + case 'È': + case 'Ê': + case 'Ë': ret='E'; break; + case 'ô': ret=138; break; + case 'ò': + case 'ö': ret='o'; break; + case 'ÿ': ret='y'; break; + case 'Ò': + case 'Ô': + case 'Ö': ret='O'; break; + case 'î': ret=141; break; + case 'ì': + case 'ï': ret='i'; break; + case 'Ì': + case 'Î': + case 'Ï': ret='I'; break; + default: return -2; + } + return ret-33; +} + +Font::Font(const Fontdata& f): + fdata_original(f), + fdata(f) { +} + +Font::Font(const Fontdata& f, const Palette& dst, int r, int g, int b, int r2, int g2, int b2): + fdata_original(f), + fdata(f) { + colorize(dst, r,g,b,r2,g2,b2); +} + +void Font::colorize(const Palette& dst, int r, int g, int b, int r2, int g2, int b2) { + Byte i; + Remap *map = new Remap(dst); + for(i=0; i<8; i++) + map->findrgb(i, (Byte) ((r2*(7-i)+r*i)/7), (Byte) ((g2*(7-i)+g*i)/7), (Byte) ((b2*(7-i)+b*i)/7)); + remap(map); + delete map; +} + +void Font::remap(const Remap *map) { + for(int i=0; iheight; y++) + for(int x=0; xwidth; x++) { + Byte pel = *((*spr)[y]+x); + if(pel != 0) + fdata.spr[i]->fast_pel(x, y, map->map[pel]); + } + } + } +} + +void Font::draw(const char *m, const Bitmap& b, int x, int y) const { + int c; + if(x == CENTER) { + x = (b.width - fdata.width(m)) >> 1; + } + while(*m) { + c = fdata.translate(&m); + if(c < 0) { + c = 'i'-33; + } else { + if(fdata.spr[c]) + fdata.spr[c]->draw(b, x, y); + } + x += fdata.pre_width[c]; + } +} + +void Font::draw(const char *m, const Video_bitmap* b, int x, int y) const { + int c; + if(x == CENTER) { + x = (b->width - fdata.width(m)) >> 1; + } + while(*m) { + c = fdata.translate(&m); + if(c < 0) { + c = 'i'-33; + } else { + if(fdata.spr[c]) + fdata.spr[c]->draw(b, x, y); + } + x += fdata.pre_width[c]; + } +} diff --git a/skelton/common/stringtable.cpp b/skelton/common/stringtable.cpp new file mode 100644 index 0000000..f3a93d4 --- /dev/null +++ b/skelton/common/stringtable.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include "stringtable.h" + +Stringtable *stringtable = NULL; + +Stringtable::Stringtable(const char *nam) { + mustfree=true; + Res_doze res(nam); + buf = new char[res.size()+1]; + memcpy(buf, res.buf(), res.size()); + parse(buf, res.size()); +} + +Stringtable::Stringtable(Byte *b, Dword size) { + mustfree=false; + parse((char *)b, size); +} + +Stringtable& Stringtable::operator=(const Stringtable& src) { + delete table; + if(mustfree) + delete buf; + mustfree=true; + num = src.size(); + int i; + //calculate space needed in buf + Dword siz=0; + for(i=0; i +#include "unicode.h" + +Unicode::Unicode(char *s): the_string(strlen(s)*2) { + Dword i; + for(i=0; i +#include +#include +//Pour isalpha: +#include +#include "url.h" +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif + +Url::Url(const char* u) { + setFull(u); +} + +const char* Url::getScheme() const { + return scheme; +} + +const char* Url::getHost() const { + return host; +} + +Word Url::getPort() const { + return port; +} + +const char* Url::getPath() const { + return path; +} + +void Url::getFull(char* buf) const { + buf[0]=0; + if(strlen(getScheme())) { + strcat(buf, getScheme()); + strcat(buf, "://"); + } + strcat(buf, getHost()); + if(getPort()) { + char n[16]; + sprintf(n, ":%i", getPort()); + strcat(buf, n); + } + strcat(buf, "/"); + strcat(buf, getPath()); +} + +void Url::setScheme(const char* s) { + if(strlen(s)sizeof(rest)) { + //Url too long, fuck it + strcpy(scheme, ""); + strcpy(host, ""); + strcpy(path, ""); + } + else + strcpy(rest, u); + char* sep; + //Find fragment + sep=strchr(rest, '#'); + if(sep) { + //Keep the frament for later (when we set the path) + strcpy(fragment, sep); + *sep=0; //Remove fragment + } + else { + //No fragment + fragment[0]=0; + } + //Look for scheme + //NB: As opposed to RFC 1808, we don't consider numbers, '+', + // '-' or '.' to be legal in scheme names. It just wouldn't + // feel right (if everybody followed standards, life would + // be boring anyway) + sep=strchr(rest, ':'); + if(sep) { + bool legalscheme=true; + char* p; + for(p=rest; p +#endif +#include "utils.h" +#include "error.h" +#include "input.h" +#include "main.h" +#include "cursor.h" +#include "net.h" +#include "video_dumb.h" + +bool video_is_dumb=false; //Defaults to false + +Dumb_Video_bitmap* Dumb_Video_bitmap::New(const int px, const int py, + const int w, const int h, + const int rw) { + return new Dumb_Video_bitmap(px, py, w, h, rw); +} + +Dumb_Video_bitmap* Dumb_Video_bitmap::New(const int px, const int py, + const int w, const int h) { + return new Dumb_Video_bitmap(px, py, w, h); +} + +Dumb_Video_bitmap::Dumb_Video_bitmap(const int px, const int py, + const int w, const int h, + const int rw) { + width = w; + height = h; + pos_x = px; + pos_y = py; +} + +Dumb_Video_bitmap::Dumb_Video_bitmap(const int px, const int py, + const int w, const int h) { + width = w; + height = h; + pos_x = px; + pos_y = py; +} + +void Dumb_Video_bitmap::rect(const int x, const int y, + const int w, const int h, + const int color) const { + clip(x, y, w, h); +} + +void Dumb_Video_bitmap::box(const int x, const int y, + const int w, const int h, + const int color) const { +} + +void Dumb_Video_bitmap::get_bitmap(const Bitmap* bit, const int x, const int y, + const int w, const int h) const { + clip(x, y, w, h); +} + +void Dumb_Video_bitmap::put_pel(const int x, const int y, const Byte c) const { + clip(x, y, 1, 1); +} + +void Dumb_Video_bitmap::hline(const int y, const int x, + const int w, const Byte c) const { + clip(x, y, w, 1); +} + +void Dumb_Video_bitmap::vline(const int x, const int y, const int h, + const Byte c) const { + clip(x, y, 1, h); +} + +void Dumb_Video_bitmap::line(const int x1, const int y1, +const int x2, const int y2, const Byte c) const { + clip(x1, y1, x2, y2); +} + +void Dumb_Video_bitmap::put_bitmap(const Bitmap& d, const int dx, + const int dy) const { + clip(dx, dy, d.width, d.height); +} + +void Dumb_Video_bitmap::put_sprite(const Sprite& d, const int dx, + const int dy) const { + int dx2 = dx - d.hot_x; + int dy2 = dy - d.hot_y; + + clip(dx2, dy2, d.width, d.height); +} + +void Dumb_Video_bitmap::setmem() { +} + +#ifdef UGS_DIRECTX +LRESULT CALLBACK dumbwindowproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch(msg) { + case WM_DESTROY: + PostQuitMessage(0); + break; +/* case WM_CHAR: + if(input) + ((Input_DX *) input)->add_key_buf((char) wparam); + return 0;*/ +/* case WM_KEYDOWN: + if(input) { + if(wparam == 19) // touche 'pause' + input->pause = true; + if(wparam >= 16 && wparam <= 46) + ((Input_DX *) input)->add_key_buf((char) wparam, true); + } + return 0;*/ + case WM_USER: + if(net) { + int err = WSAGETASYNCERROR(lparam); + net->gethostbyname_completed(err == 0); + } + return 0; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} +#endif + +Video_Dumb* Video_Dumb::New(int w, int h, int b, const char *wname) { + return new Video_Dumb(w, h, b, wname); +} + +Video_Dumb::Video_Dumb(int w, int h, int b, const char *wname) { + video_is_dumb=true; + +#ifdef UGS_LINUX + setuid(getuid()); + setgid(getgid()); + seteuid(getuid()); + setegid(getgid()); +#endif + + xwindow = false; + width = w; + height = h; + bit = b; + framecount = 0; + newpal = true; + need_paint = 2; + +#ifdef UGS_DIRECTX + BOOL rc; + WNDCLASS wc; + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = dumbwindowproc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinst; + wc.hIcon = LoadIcon(hinst, "window.ico"); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "SkeltonClass"; + rc = RegisterClass(&wc); + if(!rc) + new Error("Can't register class"); + hwnd = CreateWindowEx(0, "SkeltonClass", wname, WS_MINIMIZE|WS_POPUPWINDOW|WS_CAPTION|WS_SYSMENU, 100, 100, 200, 50, NULL, NULL, hinst, NULL); + if(hwnd == NULL) + new Error("Can't create window"); +#endif + + vb = Dumb_Video_bitmap::New(0, 0, w, h); +} + +Video_Dumb::~Video_Dumb() { + if(vb) + delete vb; +#ifdef UGS_DIRECTX + ShowWindow(hwnd, SW_HIDE); + DestroyWindow(hwnd); + UnregisterClass("SkeltonClass", hinst); +#endif +} + +void Video_Dumb::lock() { + vb->setmem(); +} + +void Video_Dumb::unlock() { +} + +void Video_Dumb::flip() { + if(newpal) { + pal.set(); + newpal = false; + } + +#ifdef UGS_LINUX + sleep(0); +#endif + + framecount++; +} + +void Video_Dumb::setpal(const Palette& p) { + pal = p; + newpal=true; +} + +void Video_Dumb::dosetpal(PALETTEENTRY pal[256], int size) { +} + +void Video_Dumb::start_frame() { + lock(); + if(cursor) { + cursor->put_back(); + cursor->move(); + } +} + +void Video_Dumb::end_frame() { + if(cursor) { + cursor->get_back(); + cursor->draw(); + } + flip(); +} + +void Video_Dumb::restore() { + newpal = true; + need_paint = 2; +} + +void Video_Dumb::clean_up() { +} + +void Video_Dumb::snap_shot(int x, int y, int w, int h) { +} diff --git a/skelton/common/zone_text_clock.cpp b/skelton/common/zone_text_clock.cpp new file mode 100644 index 0000000..75338b0 --- /dev/null +++ b/skelton/common/zone_text_clock.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "zone_text_clock.h" + +Zone_text_clock::Zone_text_clock(Inter* in, Dword *s, int px, int py, int pw, bool frame, Font *f2): + Zone_text_field(in, (int *)s, px, py, pw, f2) { + draw_frame = frame; +} + +Zone_text_clock::~Zone_text_clock() { +} + +void Zone_text_clock::draw() { + Zone_panel::draw(); + if(!draw_frame) { + video->vb->hline(y, x, w, 255); + video->vb->hline(y+h-1, x, w, 0); + video->vb->vline(x, y, h, 255); + video->vb->vline(x+w-1, y, h, 0); + video->vb->rect(x+1, y+1, w-2, h-2, 210); + } + + int seconde = *var % 60; + int minute = (*var / 60) % 60; + int heure = *var / 60 / 60; + sprintf(timebuf, "%02i:%02i:%02i", heure, minute, seconde); + font->draw(timebuf, pan, w - font->width(timebuf) - 3, 0); // alignement du texte a droite +} diff --git a/skelton/config/.cvsignore b/skelton/config/.cvsignore new file mode 100644 index 0000000..c95dcac --- /dev/null +++ b/skelton/config/.cvsignore @@ -0,0 +1 @@ +local.mk diff --git a/skelton/config/common.mk b/skelton/config/common.mk new file mode 100644 index 0000000..5952b76 --- /dev/null +++ b/skelton/config/common.mk @@ -0,0 +1,40 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +.PHONY: default all clean niceclean deps + +default: all + +TARGETS:= +CLEANS:= +DEPENDS:= +OBJECTS= + +-include config/local.mk + +include config/compiler.mk + +include $(wildcard */dir.mk) + +RULES:=now + +include $(wildcard */dir.mk) + +all: $(TARGETS) + +CLEANS+=$(shell find . -name '*.o' -print) + +clean: niceclean + rm -f .depends $(CLEANS) + +niceclean: + rm -f $(shell find . -name 'core' -print) + rm -f $(shell find . -name '*~' -print) + +.depends: + @$(foreach DEP,$(DEPENDS),$(COMPILE.cc) -M $(DEP) | sed -e 's|^.*:|$(dir $(DEP))&|' >> $@ ;) + +ifneq ($(MAKECMDGOALS),clean) +include .depends +endif + diff --git a/skelton/config/compiler.mk b/skelton/config/compiler.mk new file mode 100644 index 0000000..376d8ca --- /dev/null +++ b/skelton/config/compiler.mk @@ -0,0 +1,35 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +include config/target.mk + +ARFLAGS:=rcs +CXXFLAGS+=-pipe -pedantic -Wall -Iinclude -I../images + +OPTFLAGS+=-m486 -O6 +DEBUGFLAGS=-ggdb + +ifdef RELEASE +CXX=i386-glibc20-linux-g++ +CXXFLAGS+=-I/usr/i386-glibc20-linux/include/g++ $(OPTFLAGS) +LDFLAGS+=-L/usr/i386-glibc20-linux/lib -L/home/pp/lib/i386-glibc20-linux +else +CXXFLAGS+=$(DEBUGFLAGS) -D_DEBUG +endif + +ifdef SOCKS +CXXFLAGS+=-DSOCKS +endif + +ifeq "$(TARGET)" "linux" +CXXFLAGS+=-DUGS_LINUX -DUGS_LINUX_SVGA -DUGS_LINUX_X11 +LDLIBS+=-L/usr/X11R6/lib -lX11 -lXext -lXpm -lvga -lvgagl -lz +ifdef RELEASE +LDLIBS+=-ldb +else +LDLIBS+=-ldb1 +endif +else +# hmm, nothing else than linux is supported by this Makefile! +endif + diff --git a/skelton/config/target.mk b/skelton/config/target.mk new file mode 100644 index 0000000..3752072 --- /dev/null +++ b/skelton/config/target.mk @@ -0,0 +1,4 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +TARGET:=linux diff --git a/skelton/directx/dir.mk b/skelton/directx/dir.mk new file mode 100644 index 0000000..9acc5ec --- /dev/null +++ b/skelton/directx/dir.mk @@ -0,0 +1,21 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +ifndef MAKEFILE_INCLUDED + +.PHONY: local + +local: + $(MAKE) -C .. + +else +ifndef RULES + +ifeq "$(TARGET)" "win32" +DEPENDS+=$(wildcard directx/*.cpp) +OBJECTS+=$(patsubst %.cpp,%.o,$(wildcard directx/*.cpp)) +endif + +else +endif +endif diff --git a/skelton/directx/error.cpp b/skelton/directx/error.cpp new file mode 100644 index 0000000..1fa5fee --- /dev/null +++ b/skelton/directx/error.cpp @@ -0,0 +1,237 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "main.h" +#include "res.h" +#include "sound.h" +#include "input.h" +#include "video.h" + +#ifdef _DEBUG +int copper=0; +#endif + +#ifdef _DEBUG + bool _debug = true; +#else + bool _debug = false; +#endif + +bool skelton_debug = true; + +#ifdef _DEBUG +void COPPER(int a, int b, int c) { + if(copper) { + _outp(0x3c8,0); + _outp(0x3c9,a); + _outp(0x3c9,b); + _outp(0x3c9,c); + } +} +#endif + +void output_msg(char *m) { + static Res_dos* out=NULL; + if(!out) { + out = new Res_dos("output.txt", RES_CREATE); + if(out && !out->exist) { + delete out; + out=NULL; + } + } + OutputDebugString(m); + int siz=strlen(m); + if(m[siz-1] ==10) { + m[siz-1] = 13; + m[siz] = 10; + siz++; + } + if(out && out->exist) // pour pas planter si la creation de 'output.txt' a pas marcher! + out->write(m, siz); +} + +void lock_msgbox(const char* m, ...) { + if(_debug) { + video->unlock(); + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + output_msg(st); + video->lock(); + } +} + +void msgbox(const char* m, ...) { + if(_debug) { + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + output_msg(st); + } +} + +void skelton_msgbox(const char* m, ...) { + if(_debug && skelton_debug) { + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + output_msg(st); + } +} + +#ifdef _DEBUG +void debug_point() { + Dword tim = getmsec(); + while(getmsec() - tim < 5000) { + start_frame(); + end_frame(); + if(input && input->quel_key == DIK_F2) { // skip break point + input->quel_key = -1; + break; + } + } +} +#endif + +void user_output(const char* title, const char *msg) { + ShowCursor(TRUE); + MessageBox(NULL, msg, title, MB_ICONINFORMATION); + ShowCursor(FALSE); +} + +Error::Error(const char* m, ...) { + delete_obj(); + ShowCursor(TRUE); + MSG msg; + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + msgbox("Error::Error: %s\n", st); + MessageBox(NULL, st, "Error", MB_ICONEXCLAMATION); + exit(1); +} + +void calldx(HRESULT hr) { + if(SUCCEEDED(hr)) + return; + switch(hr) { + case DDERR_INVALIDMODE: + new Error("Invalid mode"); + case DDERR_INVALIDPARAMS: + new Error("Invalid parameters"); + case DDERR_OUTOFMEMORY: + new Error("DirectDraw out of system memory"); + case DDERR_GENERIC: + new Error("Generic DirectX error"); + case DDERR_INVALIDRECT: + new Error("Invalid rectangle specified"); + case DDERR_SURFACEBUSY: + new Error("DirectDraw surface is busy"); + case DDERR_SURFACELOST: + new Error("DirectDraw surface was lost"); + case DDERR_TOOBIGHEIGHT: + new Error("DirectDraw Object too large"); + case DDERR_OUTOFVIDEOMEMORY: + new Error("Out of video memory"); + case DDERR_INCOMPATIBLEPRIMARY: + new Error("Incompatible primary"); + case DDERR_INVALIDCAPS: + new Error("Invalid caps"); + case DDERR_INVALIDOBJECT: + new Error("Invalid object"); + case DDERR_INVALIDPIXELFORMAT: + new Error("Invalid pixel format"); + case DDERR_NOALPHAHW: + new Error("No alpha HW"); + case DDERR_NOCOOPERATIVELEVELSET: + new Error("No cooperative level set"); + case DDERR_NODIRECTDRAWHW: + new Error("No directdraw HW"); + case DDERR_NOEMULATION: + new Error("No emulation"); + case DDERR_NOEXCLUSIVEMODE: + new Error("No exclusive mode"); + case DDERR_NOFLIPHW: + new Error("No flip hw"); + case DDERR_NOMIPMAPHW: + new Error("No mipmap hw"); + case DDERR_NOZBUFFERHW: + new Error("No zbuffer Hw"); + case DDERR_PRIMARYSURFACEALREADYEXISTS: + new Error("Primary surface already exist"); + case DDERR_UNSUPPORTEDMODE: + new Error("Unsupported mode"); + case DDERR_IMPLICITLYCREATED: + new Error("Unable to perform (implicitly created)"); + case DDERR_UNSUPPORTED: + new Error("Unsupported operation"); + case DDERR_WRONGMODE: + new Error("Wrong video mode"); + case DDERR_WASSTILLDRAWING: + new Error("Directdraw still drawing"); + case DDERR_NOTAOVERLAYSURFACE: + new Error("An overlay component is called for a non-overlay surface"); + case DDERR_NOOVERLAYHW: + new Error("The operation cannot be carried out because no overlay hardware is present or available"); + case DDERR_INVALIDSURFACETYPE: + new Error("The requested operation could not be performed because the surface was of the wrong type"); + case DDERR_NOCOLORKEYHW: + new Error("Operation could not be carried out because there is no hardware support of the dest color key"); + case DI_BUFFEROVERFLOW: + new Error("Directinput buffer overflow"); + case DIERR_NOTACQUIRED: + new Error("Input not acquired"); + case DIERR_INPUTLOST: + new Error("Input lost"); + case DSERR_ALLOCATED: + new Error("The call failed because resources (such as a priority level) were already being used by another caller."); + case DSERR_CONTROLUNAVAIL: + new Error("The control (vol,pan,etc.) requested by the caller is not available."); + case DSERR_INVALIDCALL: + new Error("This call is not valid for the current state of this object"); + case DSERR_PRIOLEVELNEEDED: + new Error("The caller does not have the priority level required for the function to succeed."); + case DSERR_BADFORMAT: + new Error("The specified WAVE format is not supported"); + case DSERR_NODRIVER: + new Error("No sound driver is available for use"); + case DSERR_ALREADYINITIALIZED: + new Error("This object is already initialized"); + case DSERR_NOAGGREGATION: + new Error("This object does not support aggregation"); + case DSERR_BUFFERLOST: + new Error("The buffer memory has been lost, and must be restored."); + case DSERR_OTHERAPPHASPRIO: + new Error("Another app has a higher priority level, preventing this call from succeeding."); + case DSERR_UNINITIALIZED: + new Error("This object has not been initialized"); + case DSERR_NOINTERFACE: + new Error("The requested COM interface is not available"); + + default: + new Error("Unknown error #%i.", hr); + } +} diff --git a/skelton/directx/find_file.cpp b/skelton/directx/find_file.cpp new file mode 100644 index 0000000..671a2a0 --- /dev/null +++ b/skelton/directx/find_file.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include "main.h" +#include "find_file.h" + +Find_file_entry::Find_file_entry(const char *n, bool f) { + strncpy(name, n, 1023); + name[1023] = 0; + is_folder = f; + date[0] = 0; + size = 0; +} + +class Find_file_directx: public Find_file { + struct _finddata_t c_file; + long hFile; + bool terminated; +public: + Find_file_directx(const char *n); + virtual ~Find_file_directx(); + virtual bool eof(); + virtual Find_file_entry get_next_entry(); +}; + +Find_file *Find_file::New(const char *n) { + return new Find_file_directx(n); +} + +void Find_file::get_current_directory(char *s) { + char *w; + if(GetCurrentDirectory(1024, s) == 0) { + s[0] = 0; + } else { + do { + w = strchr(s, '\\'); + if(w) + *w = '/'; + else + break; + } while(1); + } + //Remove slashes from end + w=s+strlen(s); + while(w>=s) { + if(*w=='/') + *w=0; + else + break; + w--; + } +} + +Find_file_directx::Find_file_directx(const char *n) { + terminated = false; + if((hFile = _findfirst(n, &c_file)) == -1L) { + terminated = true; + } +} + +Find_file_directx::~Find_file_directx() { + _findclose(hFile); +} + +bool Find_file_directx::eof() { + return terminated; +} + +Find_file_entry Find_file_directx::get_next_entry() { + bool folder = (c_file.attrib & _A_SUBDIR) ? true:false; + Find_file_entry f(c_file.name, folder); + f.size = c_file.size; + strcpy(f.date, ctime(&(c_file.time_write))); + + if(_findnext(hFile, &c_file) != 0) + terminated = true; + return f; +} diff --git a/skelton/directx/input.cpp b/skelton/directx/input.cpp new file mode 100644 index 0000000..059e3b6 --- /dev/null +++ b/skelton/directx/input.cpp @@ -0,0 +1,265 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "error.h" +#include "main.h" +#include "input_dx.h" +#include "input_dumb.h" + +Input *input = NULL; + +Input* Input::New(bool dumb) { + if(dumb) + return new Input_Dumb(); + else + return new Input_DX(); +} + +const char *keynames[256]={ + "", "Escape", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", + "Backspace", "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", + "[", "]", "Return", "Ctrl", "A", "S", "D", "F", "G", "H", "J", "K", + "L", ";", "'", "`", "Left shift", "\\", "Z", "X", "C", "V", "B", "N", "M", + ",", ".", "/", "Right shift", "Pad *", "Alt", "Space", "Caps lock", + "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", + "F10", "Num lock", "Scrl lock", "Pad 7", "Pad 8", "Pad 9", + "Pad -", "Pad 4", "Pad 5", "Pad 6", "Pad +", "Pad 1", "Pad 2", + "Pad 3", "Pad 0", "Pad .", "Print scrn", "", "<", + "F11", "F12", "", "", "", "", "", "", "", + "", "", "", "", "F13", "F14", "F15", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "", "Pause 1", "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "Pad Enter", "2nd Ctrl", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "Pad /", "", "Print scrn", "2nd Alt", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "Pause 2", "Home", "Up", "Page up", + "", "Left", "", "Right", "", "End", "Down", + "Page down", "Insert", "Del", "", "", "", + "", "", "", "", "Win left", "Win right", "Win popup", "Power", "Sleep", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Macro", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" +}; + +Input_DX::Input_DX() { + lpinput = NULL; + lpinputdevice = NULL; + lpinputmouse = NULL; + calldx(DirectInputCreate(hinst, DIRECTINPUT_VERSION, &lpinput, NULL)); + calldx(lpinput->CreateDevice(GUID_SysKeyboard, &lpinputdevice, NULL)); + calldx(lpinputdevice->SetDataFormat(&c_dfDIKeyboard)); + calldx(lpinputdevice->SetCooperativeLevel(hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)); + DIPROPDWORD dipdw = { + { + sizeof(DIPROPDWORD), + sizeof(DIPROPHEADER), + 0, + DIPH_DEVICE, + }, + 16, + }; + calldx(lpinputdevice->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)); + calldx(lpinputdevice->Acquire()); + + calldx(lpinput->CreateDevice(GUID_SysMouse, &lpinputmouse, NULL)); + calldx(lpinputmouse->SetDataFormat(&c_dfDIMouse)); + calldx(lpinputmouse->SetCooperativeLevel(hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)); + + mouse_handle = NULL; + mouse.dx = mouse.dy = mouse.dz = 0; + mouse.quel = -1; + int i; + for(i=0; i<4; i++) + mouse.button[i] = 0; +/* mouse_handle = CreateEvent(0, 0, 0, 0); + if (mouse_handle == NULL) + new Error("Could not create 'event' for the mouse"); + calldx(lpinputmouse->SetEventNotification(mouse_handle)); +*/ + DIPROPDWORD dipdw2 = { + { + sizeof(DIPROPDWORD), + sizeof(DIPROPHEADER), + 0, + DIPH_DEVICE, + }, + 16, + }; + + calldx(lpinputmouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw2.diph)); + calldx(lpinputmouse->Acquire()); + quel_key = -1; + pause = false; + for(i=0; i<256; i++) + keys[i] = 0; + clear_key(); +} + +Input_DX::~Input_DX() { + if(lpinputdevice) { + lpinputdevice->Unacquire(); + lpinputdevice->Release(); + } + if(lpinputmouse) { + lpinputmouse->Unacquire(); + lpinputmouse->Release(); + } + if(lpinput) + lpinput->Release(); + if(mouse_handle) + CloseHandle(mouse_handle); +} + +void Input_DX::clear_key() { + process_key(); // vide la queue de touche + shift_key = 0; + quel_key = -1; + key_pending = 0; + for(int i=0; i<256; i++) + keys[i] = 0; +} + +void Input_DX::process_key() { + DIDEVICEOBJECTDATA od; + DWORD dwElements = 1; + Byte butt; + HRESULT hr; + for(;;) { + hr = lpinputdevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0); + if(hr == DI_BUFFEROVERFLOW) { +// new Error("Catched a buffer overflow reading keyboard"); + calldx(lpinputdevice->GetDeviceState(sizeof(keys), keys)); + clear_key(); + break; + } + if(hr == DIERR_INPUTLOST) { + restore(); + break; + } + calldx(hr); + if (dwElements == 0) + break; + butt = (Byte) (od.dwData & 0x80); + if(butt) + keys[od.dwOfs] |= PRESSED; // bit #1 -> 0:not pressed 1:pressed + else + keys[od.dwOfs] = RELEASED; // bit #2 -> 0:rien 1:was released + if(butt) { // si pese une touche + switch(od.dwOfs) { + case KEY_RSHIFT: + case KEY_LSHIFT: + shift_key |= SHIFT; + break; + case KEY_RALT: + case KEY_LALT: + shift_key |= ALT; + break; + case KEY_RCTRL: + case KEY_LCTRL: + shift_key |= CONTROL; + break; + #ifdef _DEBUG + case DIK_F10: // toggle copper (marche pas sur plusieurs carte video) + COPPER(0,0,0); + copper = !copper; + break; + case DIK_F11: // toggle msgbox() du skelton + skelton_debug = !skelton_debug; + break; + case DIK_F1: // croque la touche F1 pour les screens shots + break; + #endif + default: + quel_key = od.dwOfs; + } + } else { // si lache une touche + switch(od.dwOfs) { + case KEY_RSHIFT: + case KEY_LSHIFT: + shift_key &= ~SHIFT; + break; + case KEY_RALT: + case KEY_LALT: + shift_key &= ~ALT; + break; + case KEY_RCTRL: + case KEY_LCTRL: + shift_key &= ~CONTROL; + break; + } + } + } + if(keys[KEY_F4] & PRESSED && (keys[KEY_LALT] & PRESSED || keys[KEY_RALT] & PRESSED)) + quit_game(); + if(keys[KEY_LCTRL] && keys[DIK_NUMLOCK]) { // ignore control+num_lock car "PAUSE" conflict + keys[KEY_LCTRL] = 0; + keys[DIK_NUMLOCK] = 0; + } +} + +void Input_DX::process_mouse() { + DIDEVICEOBJECTDATA od; + DWORD dwElements = 1; + mouse.dx = mouse.dy = mouse.dz = 0; + Byte butt; + for(;;) { + HRESULT hr = lpinputmouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0); + if(hr == DIERR_INPUTLOST) { + restore(); + break; + } + calldx(hr); + if(dwElements == 0) + break; + butt = (Byte) (od.dwData & 0x80); + switch (od.dwOfs) { + case DIMOFS_X: + mouse.dx += (int) od.dwData; + break; + case DIMOFS_Y: + mouse.dy += (int) od.dwData; + break; + case DIMOFS_Z: + mouse.dz += (int) od.dwData; + break; + case DIMOFS_BUTTON0: + mouse.button[0] = (Byte) (butt ? PRESSED:RELEASED); + if(butt) + mouse.quel = 0; + break; + case DIMOFS_BUTTON1: + mouse.button[1] = (Byte) (butt ? PRESSED:RELEASED); + if(butt) + mouse.quel = 1; + break; + case DIMOFS_BUTTON2: + mouse.button[2] = (Byte) (butt ? PRESSED:RELEASED); + if(butt) + mouse.quel = 2; + break; + case DIMOFS_BUTTON3: + mouse.button[3] = (Byte) (butt ? PRESSED:RELEASED); + if(butt) + mouse.quel = 3; + break; + } + } +} + +void Input_DX::add_key_buf(char c, bool special) { + if(key_pending < MAXKEY) { + key_buf[key_pending].c = c; + key_buf[key_pending].special = special; + key_pending++; + } +} + +void Input_DX::restore() { + calldx(lpinputdevice->Acquire()); + calldx(lpinputmouse->Acquire()); + clear_key(); +} diff --git a/skelton/directx/main.cpp b/skelton/directx/main.cpp new file mode 100644 index 0000000..ea57aca --- /dev/null +++ b/skelton/directx/main.cpp @@ -0,0 +1,202 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "types.h" +#include "net.h" +#include "video.h" +#include "palette.h" +#include "input_dx.h" +#include "sound.h" +#include "cursor.h" +#include "music.h" +#include "stringtable.h" +#include "overmind.h" +#include "command.h" +#include "main.h" + +bool alt_tab=false; +Time_mode time_control=TIME_NORMAL; +void quit_game(); +HINSTANCE hinst; +HWND hwnd; + +void start_frame() { + bool quit=false; + MSG msg; + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { +COPPER(0,0,30); + if(msg.message == WM_QUIT) + alt_tab=quit=true; + TranslateMessage(&msg); + if(msg.message == WM_TIMER) { + COPPER(0,30,0); + //msgbox("WM_TIMER on frame %i\n",overmind.framecount); + } + DispatchMessage(&msg); +COPPER(0,0,30); + } +COPPER(0,0,0); + if(quit) + quit_game(); + Sleep(1); + if(!alt_tab) { + if(input) + input->check(); + if(video) + video->start_frame(); + } +} + +void end_frame() { + if(!alt_tab) { + if(video) + video->end_frame(); + } +} + +char exe_directory[_MAX_DRIVE+_MAX_DIR+1]; + +void set_path() { + char drive_buf[_MAX_DRIVE]; + char dir_buf[_MAX_DIR]; + //+3 in case they "forgot" to count the stupid double quotes + char temp_path[_MAX_PATH+3]; + strcpy(temp_path, GetCommandLine()+1); + char *find_fin = strchr(temp_path, '"'); + if(find_fin) + *find_fin = 0; + _splitpath(temp_path, drive_buf, dir_buf, NULL, NULL); + exe_directory[0] = 0; + strcat(exe_directory, drive_buf); + strcat(exe_directory, dir_buf); + //Remove useless ending \ or / + char *c=&exe_directory[strlen(exe_directory)-1]; + if(*c=='\\' || *c=='/') + *c=0; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { + hinst = hInstance; + command.add(lpCmdLine); + set_path(); + start_game(); + quit_game(); + return 0; +} + +void delete_obj() { + skelton_msgbox("overmind clean_up\n"); + overmind.clean_up(); + if(net) { + skelton_msgbox("deleting net...\n"); + delete net; + net=NULL; + } + if(sound) { + skelton_msgbox("deleting sound...\n"); + delete sound; + sound=NULL; + } + if(input) { + skelton_msgbox("deleting input...\n"); + delete input; + input=NULL; + } + if(video) { + skelton_msgbox("deleting video...\n"); + delete video; + video=NULL; + } + if(music) { + skelton_msgbox("stopping and deleting music...\n"); + music->stop(); + music->close(); + delete music; + music=NULL; + } + if(stringtable) { + skelton_msgbox("deleting stringtable...\n"); + delete stringtable; + stringtable=NULL; + } + if(cursor) { + skelton_msgbox("deleting cursor..\n"); + delete cursor; + cursor=NULL; + } + skelton_msgbox("ending delete_obj...\n"); +} + +void quit_game() { + if(video) + video->clean_up(); + delete_obj(); + exit(0); +} + +LRESULT CALLBACK windowproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +COPPER(30,0,0); +//skelton_msgbox("Frame overmind=%i, receive msg=%i\n",overmind.framecount, msg); + switch(msg) { + case WM_ACTIVATEAPP: + if(wparam && alt_tab) { + alt_tab = false; + if(input) + ((Input_DX *) input)->restore(); + if(video) + video->restore(); + ShowCursor(FALSE); + } + if(!wparam && !alt_tab) { + alt_tab = true; + if(input) + input->clear_key(); // pour eviter qu'une touche reste coller + ShowCursor(TRUE); + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_SYSKEYDOWN: + if(!alt_tab) + return 0; + break; + case WM_SYSCOMMAND: + if(!alt_tab) + return 0; + break; + case WM_CHAR: + if(input) + ((Input_DX *) input)->add_key_buf((char) wparam); + return 0; + case WM_KEYDOWN: + if(input) { + if(wparam == 19) // touche 'pause' + input->pause = true; + if(wparam >= 16 && wparam <= 46) + ((Input_DX *) input)->add_key_buf((char) wparam, true); + } + return 0; +/* case MM_MCINOTIFY: + if(wparam == MCI_NOTIFY_SUCCESSFUL && music) + music->replay(); + return 0;*/ + case WM_USER: + if(net) { + int err = WSAGETASYNCERROR(lparam); + net->gethostbyname_completed(err == 0); + } + return 0; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +Dword getmsec() { + return timeGetTime(); +} diff --git a/skelton/directx/music.cpp b/skelton/directx/music.cpp new file mode 100644 index 0000000..38da212 --- /dev/null +++ b/skelton/directx/music.cpp @@ -0,0 +1,154 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "main.h" +#include +#include "types.h" +#include "error.h" +#include "music.h" + +//#define MAX_TRACKS 100 + +class MusicWin32: public Music { + MCI_OPEN_PARMS mci_open; + int playing; + bool loop_all; +public: + MusicWin32(); + virtual void open(); + virtual void close(); + virtual void play(int quel, bool loop=false); + virtual void replay(); + virtual void stop(); +}; + +Music *music=NULL; + +Music* Music::alloc() { + return new MusicWin32; +} + +MusicWin32::MusicWin32() { + //m_nNumberOfTracks = 0; + open(); +} + +void MusicWin32::open() { + active = false; + playing = -1; + mci_open.wDeviceID = 0; + mci_open.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; + if(mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, (DWORD)&mci_open)) + return; + + MCI_SET_PARMS mciSet; + mciSet.dwTimeFormat = MCI_FORMAT_TMSF; + if(mciSendCommand(mci_open.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)&mciSet)) + return; + + active = true; +} + +void MusicWin32::close() { + if(!active) + return; + mciSendCommand(mci_open.wDeviceID, MCI_CLOSE, NULL, NULL); + active = false; +} + +/* +short Music::Read(void) +{ + int i; + short nTrackLength; + + m_nNumberOfTracks = 0; + mci_open.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; + + if(mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&mci_open)) + { + return 0; + } + + m_MCIStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + if(mciSendCommand(mci_open.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM|MCI_WAIT, (DWORD)(LPVOID)&m_MCIStatus)) + { + mciSendCommand(mci_open.wDeviceID, MCI_CLOSE, NULL, NULL); + return 0; + } + + m_nNumberOfTracks = (short)m_MCIStatus.dwReturn; + if(m_nNumberOfTracks > MAX_TRACKS) m_nNumberOfTracks = MAX_TRACKS; + m_MCIStatus.dwItem = MCI_STATUS_LENGTH; + + for(i = 0; i < m_nNumberOfTracks; i++) + { + m_MCIStatus.dwTrack = i + 1; + mciSendCommand(mci_open.wDeviceID, MCI_STATUS, MCI_TRACK|MCI_STATUS_ITEM|MCI_WAIT, (DWORD)(LPVOID)&m_MCIStatus); + nTrackLength = (short)(MCI_MSF_MINUTE(m_MCIStatus.dwReturn)*60 + MCI_MSF_SECOND(m_MCIStatus.dwReturn)); + m_nTrackLength[i] = nTrackLength; + } + + mciSendCommand(mci_open.wDeviceID, MCI_CLOSE, NULL, NULL); + + return m_nNumberOfTracks; +} + +short Music::GetTrackLength(short nTrack) +{ + if(nTrack > 0 && nTrack <= m_nNumberOfTracks) return m_nTrackLength[nTrack-1]; + else return 0; +} + +void Music::SetTrackLength(short nTrack, short nNewLength) +{ + if(nTrack > 0 && nTrack <= m_nNumberOfTracks) + m_nTrackLength[nTrack-1] = nNewLength; +} + +short Music::GetTotalLength(void) +{ + short nTotalLength = 0; + short nTrack; + + for(nTrack = 0; nTrack < m_nNumberOfTracks; nTrack++) + nTotalLength = (short)(nTotalLength + m_nTrackLength[nTrack]); + + return nTotalLength; +} +*/ +void MusicWin32::play(int quel, bool loop) { + if(!active) + return; + if(loop) + playing = 1; + else + playing = quel; + loop_all = loop; + MCI_PLAY_PARMS mciPlay; + + mciPlay.dwCallback = (DWORD) hwnd; + mciPlay.dwFrom = MCI_MAKE_TMSF(quel, 0, 0, 0); + mciPlay.dwTo = MCI_MAKE_TMSF(quel+1, 0, 0, 0); + if(loop) + mciSendCommand(mci_open.wDeviceID, MCI_PLAY, MCI_FROM /*| MCI_NOTIFY*/, (DWORD)&mciPlay); + else + mciSendCommand(mci_open.wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO/* | MCI_NOTIFY*/, (DWORD)&mciPlay); + time_control = TIME_FREEZE; +} + +void MusicWin32::replay() { + play(playing, loop_all); +} + +void MusicWin32::stop() { + if(!active || playing == -1) + return; + playing = -1; + MCI_PLAY_PARMS mciPlay; + mciPlay.dwCallback = 0; + mciSendCommand(mci_open.wDeviceID, MCI_STOP, NULL, (DWORD)&mciPlay); +} diff --git a/skelton/directx/palette.cpp b/skelton/directx/palette.cpp new file mode 100644 index 0000000..93dcd02 --- /dev/null +++ b/skelton/directx/palette.cpp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "types.h" +#include "video.h" +#include "palette.h" + +Palette noir; + +void Palette::set() { + video->dosetpal(pal, size); +} + +void Palette::load(const Image& raw) { + size=raw.palettesize(); + int j=0; + for(int i(0); isize; i++) + findrgb((Byte) i, src->r((Byte) i), src->g((Byte) i), src->b((Byte) i)); + } +} + +void Remap::findrgb(const Byte m, const Byte r, const Byte g, const Byte b) { + int best_diff=9999999, diff; + Byte best_i=0; + for(int i=1; isetpal(dest); + currentframe=destframe; +} + +void Fade::newdest(const Palette& dst, int frame) { + dest=dst; + int j=0; + for(int i(0); i<256; i++) { + delta[j]=(short) (((dest.pal[i].peRed<<7)-current[j++])/frame); + delta[j]=(short) (((dest.pal[i].peGreen<<7)-current[j++])/frame); + delta[j]=(short) (((dest.pal[i].peBlue<<7)-current[j++])/frame); + } + currentframe=0; + destframe=frame; +} + +int Fade::step() { + if(currentframe==destframe) + return 1; + else { + for(int i(0); i<768; i++) + current[i] = (short) (current[i] + delta[i]); + currentframe++; + return 0; + } +} + +void Fade::set() { + if(currentframe==destframe) + return; + if(currentframe==destframe-1) { + video->setpal(dest); + } else { + video->pal.set_size(256); + for(int i(0); i<256; i++) + video->pal.setcolor((Byte) i, (Byte) (current[i*3]>>7), (Byte) (current[i*3+1]>>7), (Byte) (current[i*3+2]>>7)); + video->newpal = true; + } +} diff --git a/skelton/directx/registry.cpp b/skelton/directx/registry.cpp new file mode 100644 index 0000000..f14e384 --- /dev/null +++ b/skelton/directx/registry.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "error.h" +#include "main.h" +#include +#include "registry.h" + +class RegistryWin32: public Registry { + char compagny[1024]; +public: + virtual void open(const char *n, const char *dir); + virtual void write(const char *k, char *v); + virtual void read(const char *k, char *buffer, unsigned long size); + virtual void close(); +}; + + +Registry *Registry::alloc() { + return new RegistryWin32(); +} + +void RegistryWin32::open(const char *n, const char *dir) { + sprintf(compagny, "Software\\Ludus Design\\%s", n); +} + +void RegistryWin32::write(const char *k, char *v) { + HKEY hkey; + DWORD disposition; + if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, compagny, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &disposition) == ERROR_SUCCESS) { + RegSetValueEx(hkey, k, 0, REG_SZ, (Byte *) v, strlen(v)); + RegCloseKey(hkey); + } else { + skelton_msgbox("RegistryWin32::write: failed to create key in [%s]/[%s] with value [%s].\n", compagny, k, v); + } +} + +void RegistryWin32::read(const char *k, char *buffer, unsigned long size) { + HKEY hkey; + skelton_msgbox("RegistryWin32::read: key [%s]/[%s] ", compagny, k); + if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, compagny, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS) { + skelton_msgbox("opened "); + unsigned long type; + if(RegQueryValueEx(hkey, k, 0, &type, (Byte *) buffer, &size) == ERROR_SUCCESS) { + skelton_msgbox("Query: value is [%s]\n",buffer); + } else { + skelton_msgbox("failed to query value\n"); + } + RegCloseKey(hkey); + } else { + skelton_msgbox("failed to open\n"); + } +} + +void RegistryWin32::close() { +} diff --git a/skelton/directx/res.cpp b/skelton/directx/res.cpp new file mode 100644 index 0000000..f0c3d6d --- /dev/null +++ b/skelton/directx/res.cpp @@ -0,0 +1,99 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "res.h" + +Res_mem::Res_mem() { + pos = 0; + _buf = NULL; +} + +#ifdef ONVEUTDESRESDOZEPOCHES +Res_doze::Res_doze(LPCTSTR lpName) { + HGLOBAL hResData; + if((hResInfo = FindResource(NULL, lpName, "CUSTOM")) == NULL) + new Error("Can't find resource '%s'", lpName); + if((hResData = LoadResource(NULL, hResInfo)) == NULL) + new Error("Can't load resource '%s'", lpName); + if((_buf = (Byte *) LockResource(hResData)) == NULL) + new Error("Can't lock resource '%s'", lpName); +} +#endif + +Res_dos::Res_dos(const char *fil, Res_mode mode) { + int flag; + _buf = NULL; + exist = true; + switch(mode) { + case RES_READ: + case RES_TRY: + flag = O_RDONLY; + break; + case RES_WRITE: + flag = O_RDWR; + break; + case RES_CREATE: + flag = O_CREAT|O_TRUNC|O_RDWR; + break; + } + handle = open(fil, O_BINARY | flag, S_IREAD | S_IWRITE); + if(handle == -1) { + if(mode == RES_TRY || mode == RES_CREATE) + exist = false; + else + new Error("Unable to open dos file '%s'", fil); + } +} + +Res_dos::~Res_dos() { + if(handle != -1) + close(handle); + if(_buf) + delete _buf; +} + +Dword Res_dos::size() { + return _filelength(handle); +} + + +void Res_dos::position(Dword po) { + lseek(handle, po, SEEK_SET); +} + +int Res_dos::read(void *b, int nb) { + Error* error; + int n = ::read(handle, b, nb); + if(n < 0) + error = new Error("Error reading file"); + return n; +} + +void Res_dos::write(const void *b, int nb) { + Error* error; + if(::write(handle, b, nb) != nb) + error = new Error("Error writing file"); +} + +const void* Res_dos::buf() { + if(_buf) + return _buf; + Error* error; + _buf = new Byte[size()]; + if(_buf == NULL) + error = new Error("Not enough memory to load file"); + read(_buf, size()); + return _buf; +} + +bool Res_dos::eof() { + return _eof(handle) ? true:false; +} + +Dword Res_dos::get_position() { + return _tell(handle); +} diff --git a/skelton/directx/sound.cpp b/skelton/directx/sound.cpp new file mode 100644 index 0000000..4005135 --- /dev/null +++ b/skelton/directx/sound.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "error.h" +#include "main.h" +#include "sound.h" + +Sound *sound = NULL; + +Sound::Sound() { + active=false; + lpds=NULL; + if(FAILED(DirectSoundCreate(NULL, &lpds, NULL))) { + return; + } else { + if(FAILED(lpds->SetCooperativeLevel(hwnd, DSSCL_PRIORITY))) { + skelton_msgbox("Sound::Sound: Failed to obtain DSSCL_PRIORITY. Trying DSSCL_NORMAL...\n"); + if(FAILED(lpds->SetCooperativeLevel(hwnd, DSSCL_NORMAL))) { + skelton_msgbox("Sound::Sound: Failed DSSCL_NORMAL!\n"); + return; + } + } + } + + DSBUFFERDESC dsBD; + IDirectSoundBuffer *lpPrimary; + ZeroMemory(&dsBD, sizeof(DSBUFFERDESC)); + dsBD.dwSize = sizeof(dsBD); + dsBD.dwFlags = DSBCAPS_PRIMARYBUFFER; + lpds->CreateSoundBuffer(&dsBD, &lpPrimary, NULL); + + WAVEFORMATEX format; + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = 2; // Assume stereo. + format.nBlockAlign = 2*format.nChannels; + format.wBitsPerSample = 16; // Assume 16-bit + format.nSamplesPerSec = 44100; // Assume 44.1 kHz + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + if(FAILED(lpPrimary->SetFormat(&format))) + skelton_msgbox("Sound::Sound: Failed to SetFormat() to 16bit 44kHz stereo. Ignoring\n"); + lpPrimary->Play(0, 0, DSBPLAY_LOOPING); + + active=true; +} + +void Sound::delete_sample(Sample *sam) { +} + +Sound::~Sound() { + if(lpds) + lpds->Release(); +} + +Sample::Sample(Res& re, int nb) { + iAlloc=0; + if(!sound) + return; + LPWAVEFORMATEX pWaveHeader; + BYTE *pbData; + UINT cbData; + void *buf = (void*)re.buf(); + DSParseWaveResource(buf, &pWaveHeader, &pbData, (DWORD *) &cbData); + int i; + iCurrent = 0; + iAlloc = nb; + pbWaveData = pbData; + cbWaveSize = cbData; + buffers[0] = DSLoadSoundBuffer(buf); + for(i=1; ilpds->DuplicateSoundBuffer(buffers[0], &buffers[i])); + buffers[i] = DSLoadSoundBuffer(buf); + } +} + +Sample::~Sample() { + int i; + for (i=0; iRelease(); + } +} + +/* +void Sample::DSReloadSoundBuffer(IDirectSoundBuffer *pDSB, LPCTSTR lpName) { + BYTE *pbWaveData; + DWORD cbWaveSize; + DSGetWaveResource(lpName, NULL, &pbWaveData, &cbWaveSize); + calldx(IDirectSoundBuffer_Restore(pDSB)); + DSFillSoundBuffer(pDSB, pbWaveData, cbWaveSize); +} +*/ +IDirectSoundBuffer *Sample::DSLoadSoundBuffer(void *res) { + IDirectSoundBuffer *pDSB = NULL; + DSBUFFERDESC dsBD = {0}; + BYTE *pbWaveData; + + DSParseWaveResource(res, &dsBD.lpwfxFormat, &pbWaveData, &dsBD.dwBufferBytes); + dsBD.dwSize = sizeof(dsBD); + dsBD.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2; + calldx(sound->lpds->CreateSoundBuffer(&dsBD, &pDSB, NULL)); + if(pDSB == NULL) + new Error("Can't create sound buffer"); + DSFillSoundBuffer(pDSB, pbWaveData, dsBD.dwBufferBytes); + return pDSB; +} + +void Sample::DSFillSoundBuffer(IDirectSoundBuffer *pDSB, BYTE *pbWaveData, DWORD cbWaveSize) { + if(pbWaveData && cbWaveSize) { + LPVOID pMem1, pMem2; + DWORD dwSize1, dwSize2; + calldx(pDSB->Lock(0, cbWaveSize, &pMem1, &dwSize1, &pMem2, &dwSize2, 0)); + CopyMemory(pMem1, pbWaveData, dwSize1); + if(0 != dwSize2) + CopyMemory(pMem2, pbWaveData+dwSize1, dwSize2); + pDSB->Unlock(pMem1, dwSize1, pMem2, dwSize2); + } else + new Error("Can't fill sound buffer"); +} + +void Sample::DSParseWaveResource(void *res, WAVEFORMATEX **ppWaveHeader, BYTE **ppbWaveData,DWORD *pcbWaveSize) { + DWORD *pdw; + DWORD *pdwEnd; + DWORD dwRiff; + DWORD dwType; + DWORD dwLength; + if(ppWaveHeader) + *ppWaveHeader = NULL; + if(ppbWaveData) + *ppbWaveData = NULL; + if(pcbWaveSize) + *pcbWaveSize = 0; + pdw = (DWORD *)res; + dwRiff = *pdw++; + dwLength = *pdw++; + dwType = *pdw++; + + if(dwRiff != mmioFOURCC('R', 'I', 'F', 'F')) + new Error("WAV file not 'RIFF' format"); + + if(dwType != mmioFOURCC('W', 'A', 'V', 'E')) + new Error("Not a WAV file!"); + + pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4); + + while(pdw < pdwEnd) { + dwType = *pdw++; + dwLength = *pdw++; + switch(dwType) { + case mmioFOURCC('f', 'm', 't', ' '): + if(ppWaveHeader && !*ppWaveHeader) { + if(dwLength < sizeof(WAVEFORMAT)) + new Error("Not a WAV file! (2)"); + + *ppWaveHeader = (WAVEFORMATEX *)pdw; + if((!ppbWaveData || *ppbWaveData) && (!pcbWaveSize || *pcbWaveSize)) + return; + } + break; + + case mmioFOURCC('d', 'a', 't', 'a'): + if((ppbWaveData && !*ppbWaveData) || (pcbWaveSize && !*pcbWaveSize)) { + if(ppbWaveData) + *ppbWaveData = (LPBYTE)pdw; + if(pcbWaveSize) + *pcbWaveSize = dwLength; + if(!ppWaveHeader || *ppWaveHeader) + return; + } + break; + } + pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1)); + } + new Error("Error parsing WAV file"); +} + +IDirectSoundBuffer *Sample::getfreebuffer() { + IDirectSoundBuffer *pDSB = buffers[iCurrent]; + if(pDSB) { + DWORD dwStatus; + calldx(pDSB->GetStatus(&dwStatus)); + if(dwStatus & DSBSTATUS_PLAYING) { + if(iAlloc > 1) { + if(++iCurrent >= iAlloc) + iCurrent = 0; + pDSB = buffers[iCurrent]; + calldx(pDSB->GetStatus(&dwStatus)); + if(dwStatus & DSBSTATUS_PLAYING) { + pDSB->Stop(); + pDSB->SetCurrentPosition(0); + } + } + if(pDSB && (dwStatus & DSBSTATUS_BUFFERLOST)) { + calldx(pDSB->Restore()); + DSFillSoundBuffer(pDSB, pbWaveData, cbWaveSize); + } + } + } + return pDSB; +} + +void Sample::stop() { + int i; + for (i=0; iStop(); + buffers[i]->SetCurrentPosition(0); + } +} + +Sfx::Sfx(Sample *sam, Dword dwPlayFlags, int vo, int pa, int f, int pos) { + if(!sound || !sam || !sound->active) + return; + buf = sam->getfreebuffer(); + if(buf != NULL) { + if(pos != -1) + position(pos); + if(f != -1) + freq(f); + if(vo != -1) + volume(vo); + if(pa != -1) + pan(pa); + calldx(buf->Play(0, 0, dwPlayFlags)); + } +} + +void Sfx::pan(int pa) { //-4000=gauche 0=centre 4000=droite + calldx(buf->SetPan(pa)); +} + +void Sfx::freq(int pa) { //200=bas 60000=tres haute + calldx(buf->SetFrequency(pa)); +} + +void Sfx::volume(int pa) { //0=full .. -4000=absent + calldx(buf->SetVolume(pa)); +} + +void Sfx::position(int pa) { + calldx(buf->SetCurrentPosition(pa)); +} diff --git a/skelton/directx/spawn.cpp b/skelton/directx/spawn.cpp new file mode 100644 index 0000000..c61047e --- /dev/null +++ b/skelton/directx/spawn.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include "types.h" +#include "error.h" +#include "spawn.h" + +class ProcessWin32: public Process { + PROCESS_INFORMATION pi; + unsigned long exit_code; +public: + ProcessWin32(); + virtual void init(const char *fn, int argc, char *argv[]); + virtual bool done(); + virtual int get_exit_code(); +}; + + +Process *Process::alloc() { + return new ProcessWin32(); +} + +ProcessWin32::ProcessWin32() { + memset(&pi, 0, sizeof(pi)); +} + +void ProcessWin32::init(const char *fn, int argc, char *argv[]) { + STARTUPINFO si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = NULL; + si.hStdOutput = NULL; + si.hStdError = NULL; + char full_cmd[2048]; + strcpy(full_cmd, fn); + int i; + for(i=0; i +#undef DIRECTDRAW_VERSION +#define DIRECTDRAW_VERSION 0x0300 +#include + +#include "input.h" +#include "main.h" +#include "cursor.h" +#include "bitmap.h" +#include "palette.h" +#include "sprite.h" +#include "video_dx.h" + +extern LRESULT CALLBACK windowproc(HWND hwnd, UINT msg, + WPARAM wparam, LPARAM lparam); + +/* singleton interne */ +DirectX_Video *directx_video = NULL; + +DirectX_Video::DirectX_Video(int w, int h, int b, const char *wname) { + xwindow = false; + directx_video = this; + ShowCursor(FALSE); + width=w; + height=h; + bit=b; + lpddsprimary=NULL; + lpddsback=NULL; + lpdd=NULL; + lpddpal=NULL; + BOOL rc; + WNDCLASS wc; + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = windowproc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinst; + wc.hIcon = LoadIcon(hinst, "window.ico"); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "SkeltonClass"; + rc = RegisterClass(&wc); + if(!rc) + new Error("Can't register class"); + hwnd = CreateWindowEx(WS_EX_TOPMOST , "SkeltonClass", wname, WS_POPUP|WS_SYSMENU, 0, 0, 0, 0, NULL, NULL, hinst, NULL); + if(hwnd == NULL) + new Error("Can't create window"); + + ddsdlock.lpSurface=NULL; + calldx(DirectDrawCreate(NULL, &lpdd, NULL)); + calldx(lpdd->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN|DDSCL_ALLOWREBOOT|DDSCL_ALLOWMODEX)); + calldx(lpdd->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN|DDSCL_ALLOWREBOOT|DDSCL_ALLOWMODEX)); + calldx(lpdd->SetDisplayMode(width, height, bit)); + + DDSCAPS ddscaps; + memset(&ddscaps, 0, sizeof(ddscaps)); + DDSURFACEDESC ddsd; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize=sizeof(ddsd); + ddsd.dwFlags=DDSD_CAPS|DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount=1; + calldx(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)); + ddscaps.dwCaps=DDSCAPS_BACKBUFFER; + calldx(lpddsprimary->GetAttachedSurface(&ddscaps, &lpddsback)); + add_surface(lpddsprimary, NULL); + + if(bit == 8) { + create_palette(); + calldx(lpddsprimary->SetPalette(lpddpal)); + newpal=TRUE; + } + framecount = 0; + need_paint=2; + + lock(); + pitch = ddsdlock.lPitch; + unlock(); + vb = Video_bitmap::New(0, 0, width, height, pitch); +} + +DirectX_Video::~DirectX_Video() { + delete vb; + surfaces.deleteall(); + if(lpdd) { + if(lpddsprimary) { + unlock(); + lpddsprimary->Release(); + lpddsprimary=NULL; + } + if(lpddpal) { + lpddpal->Release(); + lpddpal=NULL; + } + lpdd->Release(); + lpdd=NULL; + } + ShowWindow(hwnd, SW_HIDE); + DestroyWindow(hwnd); + UnregisterClass("SkeltonClass", hinst); + ShowCursor(TRUE); + directx_video = NULL; +} + +void DirectX_Video::create_palette() { + PALETTEENTRY tmp[256]; + for(int i=0; i<256; i++) { + tmp[i].peRed = 0; + tmp[i].peGreen = 0; + tmp[i].peBlue = 0; + tmp[i].peFlags = NULL; + } + calldx(lpdd->CreatePalette(DDPCAPS_8BIT|DDPCAPS_ALLOW256, tmp, &lpddpal, NULL)); +} + +void DirectX_Video::lock() { + if(ddsdlock.lpSurface) + return; + ddsdlock.dwSize=sizeof(ddsdlock); + ddsdlock.dwFlags=DDSD_ALL; + HRESULT hr; + lpddsback->Unlock(NULL); + hr = lpddsback->Lock(NULL, &ddsdlock, DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR, NULL); + if(hr == DDERR_SURFACELOST) { + skelton_msgbox("Backbuffer is lost..."); + lpddsprimary->Restore(); + if(lpddsprimary->IsLost() == DDERR_SURFACELOST) { + skelton_msgbox("and front buffer too! Aborting. This surface is not restorable\n"); + (void) new Error("Video surface is not restorable.\nIt seems to be a buggy video driver.\nPlease update DirectX!"); + } else { + skelton_msgbox(" but front buffer is not! (Buggy DirectX driver) Reconstructing video surfaces...\n"); + + remove_surface(lpddsprimary, NULL); + lpddsprimary->Release(); + DDSURFACEDESC ddsd; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize=sizeof(ddsd); + ddsd.dwFlags=DDSD_CAPS|DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount=1; + calldx(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)); + DDSCAPS ddscaps; + memset(&ddscaps, 0, sizeof(ddscaps)); + ddscaps.dwCaps=DDSCAPS_BACKBUFFER; + calldx(lpddsprimary->GetAttachedSurface(&ddscaps, &lpddsback)); + add_surface(lpddsprimary, NULL); + + lpddpal->Release(); + create_palette(); + pal.set(); + calldx(lpddsprimary->SetPalette(lpddpal)); + + + if(lpddsback->IsLost() == DDERR_SURFACELOST) { + skelton_msgbox("Unable to restore video back buffer. Aborting\n"); + (void) new Error("Video surface is not restorable.\nIt seems to be a buggy video driver.\nPlease update DirectX!"); + } + hr = lpddsback->Lock(NULL, &ddsdlock, DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR, NULL); + if(hr == DDERR_SURFACELOST) { + skelton_msgbox("Unable to lock video back buffer. Aborting\n"); + (void) new Error("Video surface is not restorable.\nIt seems to be a buggy video driver.\nPlease update DirectX!"); + } + } + } + calldx(hr); +} + +void DirectX_Video::unlock() { + if(ddsdlock.lpSurface) { + lpddsback->Unlock(NULL); + ddsdlock.lpSurface = NULL; + } +} + +void DirectX_Video::clean_up() { + if(!alt_tab) { + vb->rect(0,0,width,height,0); + flip(); + vb->rect(0,0,width,height,0); + flip(); + } +} + +void DirectX_Video::flip() { + unlock(); + + calldx(lpddsprimary->Flip(lpddsback, DDFLIP_WAIT)); + if(newpal) { + // Nouvelle methode pour palette: + int boo; + lpdd->GetVerticalBlankStatus(&boo); + if(!boo) + lpdd->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL); + pal.set(); + newpal=FALSE; + } + framecount++; +} + +void DirectX_Video::setpal(const Palette& p) { + pal = p; + newpal=TRUE; +} + +void DirectX_Video::dosetpal(PALETTEENTRY pal[256], int size) { + if(bit == 8) + calldx(lpddpal->SetEntries(0, 0, size, pal)); +} + +int DirectX_Video::screen_shot = 0; + +void DirectX_Video::end_frame() { + if(cursor) { + cursor->get_back(); + cursor->draw(); + } + if(_debug) { // screen shot + if(input) { + if(input->keys[DIK_F1] & RELEASED) { + input->keys[DIK_F1] = 0; + snap_shot(0,0, width, height); + } + } + } + + flip(); +} + +void DirectX_Video::snap_shot(int x, int y, int w, int h) { + Raw raw(w, h, 256); + char st[32]; + sprintf(st,"shot%02i.raw",screen_shot++); + skelton_msgbox("Video::snap_shot: file [%s], x=%i, y=%i, w=%i, h=%i...", st, x, y, w, h); + Res_dos res(st, RES_CREATE); + if(!res.exist) { + skelton_msgbox("Can't create file!\n"); + return; + } + raw.write(res); + int i; + char pa[3]; + for(i=0; i<256; i++) { + pa[0] = pal.r(i); + pa[1] = pal.g(i); + pa[2] = pal.b(i); + res.write(pa, 3); + } + lock(); + for(i=y; isetmem(); + +COPPER(30,0,30); + if(cursor) { + cursor->put_back(); + cursor->move(); + } +} + +void DirectX_Video::add_surface(LPDIRECTDRAWSURFACE s, Bitmap *b) { + surfaces.add(new DirectX_Surface(s, b)); +} + +void DirectX_Video::remove_surface(LPDIRECTDRAWSURFACE s, Bitmap *b) { + for(int i=0; is && b == surfaces[i]->b) { + delete surfaces[i]; + surfaces.remove(i); + break; + } + } +} + +void DirectX_Video::restore() { + for(int i=0; is; + if(s->IsLost() == DDERR_SURFACELOST) { + calldx(s->Restore()); + } + } + newpal=TRUE; + need_paint=2; +} + +DirectX_Video_bitmap::DirectX_Video_bitmap(const int px, const int py, const int w, const int h, const int rw) { + width = w; + height = h; + pos_x = px; + pos_y = py; + currentpage = new Bitmap(NULL, w, h, rw); +} + +DirectX_Video_bitmap::DirectX_Video_bitmap(const int px, const int py, const int w, const int h) { + width = w; + height = h; + pos_x = px; + pos_y = py; + currentpage = new Bitmap(NULL, w, h, video->pitch); + setmem(); +} + +DirectX_Video_bitmap::~DirectX_Video_bitmap() { + delete currentpage; +} + +void DirectX_Video_bitmap::setmem() { + currentpage->setmem((Byte *) directx_video->ddsdlock.lpSurface + pos_y*video->pitch + pos_x); +} + +void DirectX_Video_bitmap::rect(const int x,const int y,const int w,const int h,const int color) const { + if(clip(x, y, w, h)) + return; + video->unlock(); + RECT rect; + rect.top = clip_y1+pos_y; + rect.left = clip_x1+pos_x; + rect.right = clip_x2+pos_x+1; // maudit que c'est poche + rect.bottom = clip_y2+pos_y+1; // le dernier pixel est 'exclu' bordel + DDBLTFX ddbltfx; + ddbltfx.dwSize = sizeof(ddbltfx); + ddbltfx.dwFillColor = color; + calldx(directx_video->lpddsback->Blt(&rect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx)); + video->lock(); +} + +void DirectX_Video_bitmap::box(const int x,const int y,const int w,const int h,const int color) const { + hline(y, x, w, color); + hline(y+h-1, x, w, color); + vline(x, y, h, color); + vline(x+w-1, y, h, color); +} + +void DirectX_Video_bitmap::get_bitmap(const Bitmap* bit, const int x, const int y, const int w, const int h) const { + if(clip(x, y, w, h)) + return; + Bitmap src((*currentpage)[clip_y1]+clip_x1, clip_w, clip_y2-clip_y1+1, currentpage->realwidth); + src.draw(*bit, clip_x1-x, clip_y1-y); +} + +void DirectX_Video_bitmap::put_pel(const int x, const int y, const Byte c) const { + currentpage->put_pel(x, y, c); +} + +void DirectX_Video_bitmap::hline(const int y, const int x, const int w, const Byte c) const { + currentpage->hline(y, x, w, c); +} + +void DirectX_Video_bitmap::vline(const int x, const int y, const int h, const Byte c) const { + currentpage->vline(x, y, h, c); +} + +void DirectX_Video_bitmap::line(const int x1, const int y1, const int x2, const int y2, + const Byte c) const +{ + currentpage->line(x1, y1, x2, y2, c); +} + +void DirectX_Video_bitmap::put_bitmap(const Bitmap& d, const int dx, const int dy) const { +#if 0 + if(d.directx) { + if(clip(dx, dy, d)) + return; + + RECT rect; + rect.top = clip_y1-dy; + rect.left = clip_x1-dx; + rect.right = clip_x2-dx+1; // maudit que c'est poche + rect.bottom = clip_y2-dy+1; // le dernier pixel est 'exclu' bordel + directx_video->lpddsback->BltFast(clip_x1+pos_x, clip_y1+pos_y, d.directx_surface,&rect, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT); + } else { +#endif + d.draw(*currentpage, dx, dy); +#if 0 + } +#endif +} + +void DirectX_Video_bitmap::put_sprite(const Sprite& d, const int dx, const int dy) const { +#if 0 + if(d.directx) { + int tx = dx-d.hot_x; + int ty = dy-d.hot_y; + if(clip(tx, ty, d)) + return; + + RECT rect; + rect.top = clip_y1-ty; + rect.left = clip_x1-tx; + rect.right = clip_x2-tx+1; // maudit que c'est poche + rect.bottom = clip_y2-ty+1; // le dernier pixel est 'exclu' bordel + + directx_video->lpddsback->BltFast(clip_x1+pos_x, clip_y1+pos_y, d.directx_surface,&rect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT); + } else { +#endif + d.draw(*currentpage, dx, dy); +#if 0 + } +#endif +} diff --git a/skelton/directx/video_dx16.cpp b/skelton/directx/video_dx16.cpp new file mode 100644 index 0000000..bf2d666 --- /dev/null +++ b/skelton/directx/video_dx16.cpp @@ -0,0 +1,654 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#undef DIRECTDRAW_VERSION +#define DIRECTDRAW_VERSION 0x0300 +#include + +#include "random.h" +#include "main.h" +#include "input.h" +#include "surface.h" +#include "video_dx16.h" + +extern LRESULT CALLBACK windowproc(HWND hwnd, UINT msg, + WPARAM wparam, LPARAM lparam); + +/* singleton interne */ +static Video16 *video16 = NULL; + +Surface *screen; // pour permettre a l'applic de connaitre le back buffer + +class Surface_basedx: public Surface { +public: + LPDIRECTDRAWSURFACE surf; + Surface_basedx(int w, int h); + virtual ~Surface_basedx(); + virtual void upload(void *src, int w, int h) { } + virtual void blit(Surface *dest, int x, int y); + virtual void load24rgb(Res &res, Dither_mode dither=NONE) { } + virtual void load24rgb(void *mem, Dither_mode dither=NONE) { } + virtual void upload() { } + virtual void restore(); + virtual void start_fade() { } + virtual void end_fade() { } + virtual void fade_down(int c) { } + virtual void set_mask_color(int color) { } + virtual void rect(int x, int y, int w, int h, int color); + virtual bool clip(int *x, int *y, int *w, int *h, int *start_x=NULL, int *start_y=NULL); +}; + +Video16::Video16(int w, int h, int b, const char *wname) { + xwindow=false; + video16 = this; + ShowCursor(FALSE); + width=w; + height=h; + bit=b; + lpddsprimary=NULL; + lpddsback=NULL; + lpdd=NULL; + lpddpal=NULL; + BOOL rc; + WNDCLASS wc; + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = windowproc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinst; + wc.hIcon = LoadIcon(hinst, "window.ico"); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "SkeltonClass"; + rc = RegisterClass(&wc); + if(!rc) + new Error("Can't register class"); + hwnd = CreateWindowEx(WS_EX_TOPMOST , "SkeltonClass", wname, WS_POPUP|WS_SYSMENU, 0, 0, 0, 0, NULL, NULL, hinst, NULL); + if(hwnd == NULL) + new Error("Can't create window"); + + ddsdlock.lpSurface=NULL; + DDSURFACEDESC ddsd; + memset(&ddsd, 0, sizeof(ddsd)); + DDSCAPS ddscaps; + memset(&ddscaps, 0, sizeof(ddscaps)); + calldx(DirectDrawCreate(NULL, &lpdd, NULL)); + calldx(lpdd->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN|DDSCL_ALLOWREBOOT|DDSCL_ALLOWMODEX)); + calldx(lpdd->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN|DDSCL_ALLOWREBOOT|DDSCL_ALLOWMODEX)); + calldx(lpdd->SetDisplayMode(width, height, bit)); + + ddsd.dwSize=sizeof(ddsd); + ddsd.dwFlags=DDSD_CAPS|DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount=1; + calldx(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)); + ddscaps.dwCaps=DDSCAPS_BACKBUFFER; + calldx(lpddsprimary->GetAttachedSurface(&ddscaps, &lpddsback)); + + back = new Surface_basedx(width, height); + back->surf = lpddsback; + screen = back; + + DDPIXELFORMAT pel_format; + pel_format.dwSize = sizeof(pel_format); + lpddsprimary->GetPixelFormat(&pel_format); + msgbox("Pixel format info: RGB Bit count=%i\n", pel_format.dwRGBBitCount); + msgbox(" Bits: Red=%x Green=%x Blue=%x\n", pel_format.dwRBitMask, pel_format.dwGBitMask, pel_format.dwBBitMask); + if(pel_format.dwGBitMask >> 5 == 63) { + green_high = 1; + msgbox(" Hicolor 565 mode detected\n"); + } else { + green_high = 0; + msgbox(" Hicolor 555 mode detected\n"); + } + + framecount = 0; + need_paint=2; + + lock(); + pitch = ddsdlock.lPitch; + unlock(); + back->rect(0, 0, width, height, 0); + flip(); + back->rect(0, 0, width, height, 0); +} + +Video16::~Video16() { + delete back; + if(lpdd) { + if(lpddsprimary) { + unlock(); + lpddsprimary->Release(); + lpddsprimary=NULL; + } + lpdd->Release(); + lpdd=NULL; + } + DestroyWindow(hwnd); + UnregisterClass("SkeltonClass", hinst); + ShowCursor(TRUE); + video16 = NULL; +} + +void Video16::lock() { + if(ddsdlock.lpSurface) + return; + ddsdlock.dwSize=sizeof(ddsdlock); + ddsdlock.dwFlags=DDSD_ALL; + calldx(lpddsback->Lock(NULL, &ddsdlock, DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR, NULL)); +} + +void Video16::unlock() { + if(ddsdlock.lpSurface) { + lpddsback->Unlock(NULL); + ddsdlock.lpSurface = NULL; + } +} + +void Video16::clean_up() { + if(!alt_tab) { + //vb->rect(0,0,width,height,0); + flip(); + //vb->rect(0,0,width,height,0); + flip(); + } +} + +void Video16::flip() { + calldx(lpddsprimary->Flip(lpddsback, DDFLIP_WAIT)); + framecount++; +} + +void Video16::setpal(const Palette& p) { +} + +void Video16::dosetpal(PALETTEENTRY pal[256], int size) { +} + +int Video16::screen_shot = 0; + +void Video16::end_frame() { + if(_debug) { // screen shot + if(input) { + if(input->keys[DIK_F1] & RELEASED) { + input->keys[DIK_F1] = 0; + snap_shot(0,0, width, height); + } + } + } + + flip(); +} + +void Video16::snap_shot(int x, int y, int w, int h) { +/* Raw raw(w, h, 256); + char st[32]; + sprintf(st,"shot%02i.raw",screen_shot++); + skelton_msgbox("Video::snap_shot: file [%s], x=%i, y=%i, w=%i, h=%i...", st, x,y,w,h); + Res_dos res(st, RES_CREATE); + if(!res.exist) { + skelton_msgbox("Can't create file!\n"); + return; + } + raw.write(res); + int i; + char pa[3]; + for(i=0; i<256; i++) { + pa[0] = pal.r(i); + pa[1] = pal.g(i); + pa[2] = pal.b(i); + res.write(pa, 3); + } + lock(); + for(i=y; iRestore()); + for(int i=0; irestore(); +} + +void Video16::add_surface(Surface *s) { + surfaces.add(s); +} + +void Video16::remove_surface(Surface *s) { + surfaces.remove_item(s); +} + +Surface_basedx::Surface_basedx(int w, int h) { + width = w; + height = h; + surf = NULL; + transparent = false; +} + +Surface_basedx::~Surface_basedx() { +} + +void Surface_basedx::rect(int x, int y, int w, int h, int color) { + if(!clip(&x, &y, &w, &h)) + return; + RECT rect; + rect.left = x; + rect.top = y; + rect.right = w+x; + rect.bottom = y+h; + DDBLTFX ddbltfx; + ddbltfx.dwSize = sizeof(ddbltfx); + ddbltfx.dwFillColor = color; + calldx(surf->Blt(&rect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx)); +} + +bool Surface_basedx::clip(int *x, int *y, int *w, int *h, int *start_x, int *start_y) { + if(*x >= width || *y >= height || *x+*w<=0 || *y+*h<=0) + return false; + if(*x < 0) { + *w += *x; + if(start_x) + *start_x -= *x; + *x = 0; + } + if(*y < 0) { + *h += *y; + if(start_y) + *start_y -= *y; + *y = 0; + } + if(*x+*w > width) + *w = width - *x; + if(*y+*h > height) + *h = height - *y; + if(*w < 0 || *h < 0) + return false; + + return true; +} + +void Surface_basedx::blit(Surface *dest, int x, int y) { + int w = width; + int h = height; + int start_x = 0, start_y = 0; + if(!dest->clip(&x, &y, &w, &h, &start_x, &start_y)) + return; + RECT rect; + rect.left = start_x; + rect.top = start_y; + rect.right = start_x+w; + rect.bottom = start_y+h; + DDBLTFX ddbltfx; + ddbltfx.dwSize = sizeof(ddbltfx); + +/* ddbltfx.dwAlphaSrcConst = ugs_random.rnd2(255); + ddbltfx.dwAlphaSrcConstBitDepth = ugs_random.rnd2(7); + RECT rect2; + rect2.left = x; + rect2.top = y; + rect2.right = x+w; + rect2.bottom = y+h;*/ + + Surface_basedx *d = (Surface_basedx *) dest; + if(transparent) { +// calldx(d->surf->Blt(&rect2, surf, &rect, DDBLT_ALPHASRCCONSTOVERRIDE | DDBLT_KEYSRC | DDBLT_WAIT, &ddbltfx)); + calldx(d->surf->BltFast(x, y, surf, &rect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT)); + } else { +// calldx(d->surf->Blt(&rect2, surf, &rect, DDBLT_WAIT, &ddbltfx)); + calldx(d->surf->BltFast(x, y, surf, &rect, DDBLTFAST_WAIT)); + } +} + +void Surface_basedx::restore() { + if(surf) { + if(surf->IsLost() == DDERR_SURFACELOST) + surf->Restore(); + } +} + +class Surface_dx_sys: public Surface_basedx { +public: + Word *buffer; + bool fading; + int fade_value; + Surface_dx_sys(int w, int h); + virtual ~Surface_dx_sys(); + virtual void create_surface(); + virtual void upload(void *src, int w, int h); + virtual void load24rgb(Res &res, Dither_mode dither=NONE); + virtual void load24rgb(void *mem, Dither_mode dither=NONE); + virtual void upload(); + virtual void restore(); + virtual void start_fade(); + virtual void end_fade(); + virtual void fade_down(int c); + virtual void set_mask_color(int color); +}; + +class Surface_dx_vid: public Surface_basedx { +public: + Surface *sys_surf; + Surface_dx_vid(int w, int h); + virtual ~Surface_dx_vid(); + virtual void create_surface(); + virtual void upload(void *src, int w, int h); + virtual void load24rgb(Res &res, Dither_mode dither=NONE); + virtual void load24rgb(void *mem, Dither_mode dither=NONE); + virtual void upload(); + virtual void restore(); + virtual void start_fade(); + virtual void end_fade(); + virtual void fade_down(int c); + virtual void set_mask_color(int color); +}; + +Surface *Surface::New_video(int w, int h) { + Surface_dx_vid *s = new Surface_dx_vid(w, h); + if(!s->surf) { + delete s; + msgbox("Surface::New_video: Allocating a system memory surface instead of video.\n"); + return new Surface_dx_sys(w, h); + } else + return s; +} + +Surface *Surface::New(int w, int h) { + return new Surface_dx_sys(w, h); +} + +Surface_dx_sys::Surface_dx_sys(int w, int h): Surface_basedx(w,h) { + buffer = NULL; + fading = false; + video16->add_surface(this); + create_surface(); +} + +Surface_dx_sys::~Surface_dx_sys() { + video16->remove_surface(this); + if(surf) + surf->Release(); + if(buffer) + free(buffer); +} + +void Surface_dx_sys::create_surface() { + DDSURFACEDESC ddsd; + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY | DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + + calldx(video16->lpdd->CreateSurface(&ddsd, &surf, NULL)); +} + +void Surface_dx_sys::upload(void *src, int w, int h) { + DDSURFACEDESC lock; + lock.dwSize=sizeof(lock); + lock.dwFlags=DDSD_ALL; + calldx(surf->Lock(NULL, &lock, DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR, NULL)); + int pit_s = lock.lPitch; + int pitch = w*2; + h = min(h, lock.dwHeight); + w = min(w, lock.dwWidth); + for(int i=0; iUnlock(NULL)); +} + +void Surface_dx_sys::upload() { + if(buffer) + upload(buffer, width, height); +} + +void Surface_dx_sys::load24rgb(Res &res, Dither_mode dither) { + load24rgb((void *) res.buf(), dither); +} + +void Surface_dx_sys::load24rgb(void *mem, Dither_mode dither) { + Byte *p = (Byte *) mem; + if(!buffer) + buffer = (Word *) malloc(width*height*2); + Word *p2 = buffer; + int i,j; + if(dither == Dither_mode::NONE) { + for(j=0; j>3); + g = (*p++>>(3 - video16->green_high)); + b = (*p++>>3); + + *p2++ = (b) + ((g)<<5) + ((r)<<(10 + video16->green_high)); + } + } + } + if(dither == Dither_mode::RANDOM) { + for(j=0; j>3; + g = g2>>(3 - video16->green_high); + b = b2>>3; + r2 &= 7; + b2 &= 7; + int rnd = ugs_random.rnd(7); + if(video16->green_high && g < 63) { + g2 &= 3; + if((rnd>>1) < g2) + g++; + } else if(g < 31) { + g2 &= 7; + if(rnd < g2) + g++; + } + if(r < 31 && rnd < r2) + r++; + if(b < 31 && rnd < b2) + b++; + *p2++ = (b) + ((g)<<5) + ((r)<<(10 + video16->green_high)); + } + } + } + if(dither == Dither_mode::ORDERED) { + for(j=0; j>3; + g = g2>>(3 - video16->green_high); + b = b2>>3; + r2 &= 7; + b2 &= 7; + if(video16->green_high && g < 63) { + g2 &= 3; + if((i+j)%(g2+1) > 0) + g++; + } else if(g < 31) { + g2 &= 7; + if((i+j)%(g2+1) > 0) + g++; + } + if(r < 31 && (i+j)%(r2+1) > 0) + r++; + if(b < 31 && (i+j)%(b2+1) > 0) + b++; + *p2++ = (b) + ((g)<<5) + ((r)<<(10 + video16->green_high)); + } + } + } +} + +void Surface_dx_sys::restore() { + if(surf->IsLost() == DDERR_SURFACELOST) { + (void) new Error("Surface_dx_sys:restore: System memory surface was lost (It should NOT happen!)"); + } +} + +void Surface_dx_sys::start_fade() { + fading = true; + fade_value = 0; +} + +void Surface_dx_sys::end_fade() { + fading = false; + fade_value = 0; + upload(); +} + +void Surface_dx_sys::fade_down(int c) { + fade_value = c; + Word *in = buffer; + Word *out; + + DDSURFACEDESC lock; + lock.dwSize=sizeof(lock); + lock.dwFlags=DDSD_ALL; + calldx(surf->Lock(NULL, &lock, DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR, NULL)); + if(video16->green_high) { + for(int j=0; j>11)) - c; + g = ((*in>>5)&63) - (c<<1); + b = (*in&31) - c; + *in++; + if(r<0) + r=0; + if(g<0) + g=0; + if(b<0) + b=0; + *out++ = (b) + ((g)<<5) + ((r)<<11); + } + } + } else { + for(int j=0; j>10)) - c; + g = ((*in>>5)&31) - c; + b = (*in&31) - c; + *in++; + if(r<0) + r=0; + if(g<0) + g=0; + if(b<0) + b=0; + *out++ = (b) + ((g)<<5) + ((r)<<10); + } + } + } + calldx(surf->Unlock(NULL)); +} + +void Surface_dx_sys::set_mask_color(int color) { + trans_color = color; + transparent = true; + DDCOLORKEY dcol; + dcol.dwColorSpaceLowValue = trans_color; + dcol.dwColorSpaceHighValue = trans_color; + calldx(surf->SetColorKey(DDCKEY_SRCBLT, &dcol)); +} + +Surface_dx_vid::Surface_dx_vid(int w, int h): Surface_basedx(w,h) { + create_surface(); + if(surf) { + video16->add_surface(this); + sys_surf = Surface::New(width, height); + } +} + +Surface_dx_vid::~Surface_dx_vid() { + if(surf) { + video16->remove_surface(this); + surf->Release(); + delete sys_surf; + } +} + +void Surface_dx_vid::create_surface() { + DDSURFACEDESC ddsd; + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + + HRESULT hr = video16->lpdd->CreateSurface(&ddsd, &surf, NULL); + if(hr != DD_OK) { + msgbox("Surface_dx_vid::create_surface: Unable to create surface in video memory.\n"); + surf = NULL; + } +} + +void Surface_dx_vid::upload(void *src, int w, int h) { + sys_surf->upload(src, w, h); + sys_surf->blit(this, 0, 0); +} + +void Surface_dx_vid::upload() { + sys_surf->upload(); + sys_surf->blit(this, 0, 0); +} + +void Surface_dx_vid::load24rgb(Res &res, Dither_mode dither) { + sys_surf->load24rgb((void *) res.buf(), dither); +} + +void Surface_dx_vid::load24rgb(void *mem, Dither_mode dither) { + sys_surf->load24rgb(mem, dither); +} + +void Surface_dx_vid::restore() { + if(surf->IsLost() == DDERR_SURFACELOST) { + Surface_basedx::restore(); + sys_surf->blit(this, 0, 0); + } +} + +void Surface_dx_vid::start_fade() { + sys_surf->start_fade(); +} + +void Surface_dx_vid::end_fade() { + sys_surf->end_fade(); + sys_surf->blit(this, 0, 0); +} + +void Surface_dx_vid::fade_down(int c) { + sys_surf->fade_down(c); + sys_surf->blit(this, 0, 0); +} + +void Surface_dx_vid::set_mask_color(int color) { + trans_color = color; + transparent = true; + DDCOLORKEY dcol; + dcol.dwColorSpaceLowValue = trans_color; + dcol.dwColorSpaceHighValue = trans_color; + calldx(surf->SetColorKey(DDCKEY_SRCBLT, &dcol)); +} diff --git a/skelton/directx/video_new.cpp b/skelton/directx/video_new.cpp new file mode 100644 index 0000000..f4b313e --- /dev/null +++ b/skelton/directx/video_new.cpp @@ -0,0 +1,50 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#undef DIRECTDRAW_VERSION +#define DIRECTDRAW_VERSION 0x0300 +#include + +#include "video_dumb.h" +#include "video_dx.h" +#include "video_dx16.h" + +Video* video = NULL; +static bool video_8bit = true; + +Video_bitmap* Video_bitmap::New(const int px, const int py, + const int w, const int h, const int rw) { + if(video_is_dumb) + return Dumb_Video_bitmap::New(px, py, w, h, rw); + if(video_8bit) + return new DirectX_Video_bitmap(px, py, w, h, rw); + else + return NULL; +} + +Video_bitmap* Video_bitmap::New(const int px, const int py, + const int w, const int h) { + if(video_is_dumb) + return Dumb_Video_bitmap::New(px, py, w, h); + if(video_8bit) + return new DirectX_Video_bitmap(px, py, w, h); + else + return NULL; +} + +Video* Video::New(int w, int h, int b, const char *wname, bool dumb) { + if(dumb) + return Video_Dumb::New(w, h, b, wname); + if(b == 8) { + video_8bit = true; + return new DirectX_Video(w, h, b, wname); + } else { + video_8bit = false; + return new Video16(w, h, b, wname); + } +} diff --git a/skelton/include/array.h b/skelton/include/array.h new file mode 100644 index 0000000..4f129b9 --- /dev/null +++ b/skelton/include/array.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_ARRAY +#define _HEADER_ARRAY +#include + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +template +class Array { + std::vector v; +public: + void add(const T& t) { + v.push_back(t); + } + void add_before(const T& t, int i) { + v.insert(&v[i], t); + } + bool remove_item(const T& t) { + for(int i=0; i +#include +#include +#include "error.h" + +struct AllocNode { + void* allocation; + char* fileName; + unsigned int lineNo; + char* funcName; + char* className; + unsigned int size; + AllocNode* next; +}; + +class Debug { +private: + static AllocNode* allocs; + static bool initialized; +public: + static void init(); + static bool verify(bool cond, + const char* fileName, + unsigned int lineNo, + const char* funcName, + const char* condString); + static void assert(bool cond, + const char* fileName, + unsigned int lineNo, + const char* funcName, + const char* condString); + static void logMessage(const char* fileName, + unsigned int lineNo, + const char* funcName, + const char* message); + static void* logAlloc(void* aObject, + const char* aClass, + unsigned int aSize, + const char* fileName, + unsigned int lineNo, + const char* funcName); + static void logFree(void* aObject, + const char* objectName, + const char* fileName, + unsigned int lineNo, + const char* funcName); + static void dumpAllocs(); +}; + +inline void Debug::init() { + if(!initialized) { + atexit(Debug::dumpAllocs); + initialized = true; + } +} + +inline bool Debug::verify(bool cond, + const char* fileName, + unsigned int lineNo, + const char* funcName, + const char* condString) { + if(!cond) + if(funcName) + skelton_msgbox("%s:%i: failed verify \"%s\" in \"%s\"\n", + fileName, lineNo, condString, funcName); + else + skelton_msgbox("%s:%i: failed verify: %s\n", + fileName, lineNo, condString); + + return cond; +} + +inline void Debug::assert(bool cond, + const char* fileName, + unsigned int lineNo, + const char* funcName, + const char* condString) { + if(!cond) { + if(funcName) + skelton_msgbox("%s:%i: failed assert \"%s\" in \"%s\"\n", + fileName, lineNo, condString, funcName); + else + skelton_msgbox("%s:%i: failed assert: %s\n", + fileName, lineNo, condString); + + abort(); + } +} + +inline void Debug::logMessage(const char* fileName, + unsigned int lineNo, + const char* funcName, + const char* message) { + if(funcName) + skelton_msgbox("%s:%i: %s in \"%s\"\n", + fileName, lineNo, message, funcName); + else + skelton_msgbox("%s:%i: %s\n", + fileName, lineNo, message); +} + +inline void* Debug::logAlloc(void* aObject, + const char* aClass, + unsigned int aSize, + const char* fileName, + unsigned int lineNo, + const char* funcName) { + AllocNode* tmp; + + tmp = new AllocNode; + tmp->allocation = aObject; + tmp->fileName = strdup(fileName); + tmp->lineNo = lineNo; + if(funcName) + tmp->funcName = strdup(funcName); + else + tmp->funcName = NULL; + tmp->className = strdup(aClass); + tmp->size = aSize; + tmp->next = allocs; + allocs = tmp; + + init(); + + return aObject; +} + +inline void Debug::logFree(void* aObject, + const char* objectName, + const char* fileName, + unsigned int lineNo, + const char* funcName) { + bool found; + AllocNode* ptr; + AllocNode** prev; + + found = false; + + ptr = allocs; + prev = &allocs; + + while(ptr) { + if(ptr->allocation == aObject) { + found = true; + *prev = ptr->next; + delete ptr; + break; + } + prev = &ptr->next; + ptr = ptr->next; + } + + if(!found) + if(funcName) + skelton_msgbox("%s:%i: DELETE() of unlogged object \"%s\" (%p) in \"%s\"\n", + fileName, lineNo, objectName, aObject, funcName); + else + skelton_msgbox("%s:%i: DELETE() of unlogged object \"%s\" (%p)\n", + fileName, lineNo, objectName, aObject); +} + +#if (!defined __GNUC__ || __GNUC__ < 2 || __GNUC_MINOR__ < (defined __cplusplus ? 6 : 4)) +#define __FUNCNAME__ ((const char*)0) +#else +#define __FUNCNAME__ __PRETTY_FUNCTION__ +#endif + +#define ASSERT(cond) \ +Debug::assert(cond, __FILE__, __LINE__, __FUNCNAME__, #cond) + +#define VERIFY(cond) \ +Debug::verify((cond), __FILE__, __LINE__, __FUNCNAME__, #cond) + +#define LOGMESSAGE(msg) \ +Debug::logMessage(__FILE__, __LINE__, __FUNCNAME__, msg) + +#define NEW(tclass, arg) \ +(tclass*)Debug::logAlloc(new tclass arg, #tclass, sizeof(tclass), __FILE__, __LINE__, __FUNCNAME__) + +#define DELETE(object) \ +(Debug::logFree(object, #object, __FILE__, __LINE__, __FUNCNAME__), delete object) + +#define INIT_DEBUG \ +Debug::init() + +#else /* !_DEBUG */ + +#define ASSERT(cond) ((void)0) +#define VERIFY(cond) (cond) +#define LOGMESSAGE(msg) ((void)0) +#define NEW(tclass, arg) new tclass arg +#define DELETE(object) delete object +#define INIT_DEBUG ((void)0) + +#endif /* !_DEBUG */ + +#endif /* !__HEADER_DEBUG_H */ diff --git a/skelton/include/dict.h b/skelton/include/dict.h new file mode 100644 index 0000000..7ace8bd --- /dev/null +++ b/skelton/include/dict.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_DICT +#define _HEADER_DICT + +#include "types.h" +#include "buf.h" +#include "array.h" + +class Dict { + Array sub; + char key[32]; + Textbuf value; +public: + Dict(const char *k=NULL, const char *v=NULL); + virtual ~Dict(); + void add(const char *s); + void dump() const; + Dword size() const; + const char *get_key() const; + const char *find(const char *s) const; + Dict *find_sub(const char *s) const; + Dict *get_sub(const int i) const; +}; + +#endif diff --git a/skelton/include/error.h b/skelton/include/error.h new file mode 100644 index 0000000..509c00d --- /dev/null +++ b/skelton/include/error.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_ERROR +#define _HEADER_ERROR + +extern bool _debug; +extern bool skelton_debug; + +#ifdef UGS_DIRECTX + #ifdef _DEBUG + extern int copper; + void COPPER(int a, int b, int c); + void debug_point(); + #else + #define COPPER(a,b,c) ; + #endif + void calldx(long hr); +#endif + +extern void delete_obj(); +void msgbox(const char* m, ...); +void skelton_msgbox(const char* m, ...); +void lock_msgbox(const char* m, ...); +void user_output(const char* title, const char *msg); + +class Error { +public: + Error(const char* m, ...); +}; + +#endif diff --git a/skelton/include/find_file.h b/skelton/include/find_file.h new file mode 100644 index 0000000..fa54c55 --- /dev/null +++ b/skelton/include/find_file.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_FIND_FILE +#define _HEADER_FIND_FILE + +#include "types.h" + +class Find_file_entry { +public: + char name[1024]; + bool is_folder; + Dword size; + char date[1024]; + Find_file_entry(const char *n, bool f); +}; + +class Find_file { +public: + virtual ~Find_file() { } + static Find_file* New(const char *n); + static void get_current_directory(char *s); + virtual bool eof() = 0; + virtual Find_file_entry get_next_entry() = 0; +}; + +#endif diff --git a/skelton/include/http_post.h b/skelton/include/http_post.h new file mode 100644 index 0000000..5e34109 --- /dev/null +++ b/skelton/include/http_post.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_HTTP_POST +#define _HEADER_HTTP_POST + +#include "types.h" +#include "http_request.h" + +class Http_post: public Http_request { + Buf data; + Buf url; + char cgi[256]; + void init(const char *path); +public: + Http_post(const char *host, int port, const char *path); + Http_post(Dword hostaddr, int port, const char *path); + virtual ~Http_post(); + void add_data_raw(const char* m); + void add_data_encode(const char* m, ...); + void send(); +}; + +#endif diff --git a/skelton/include/http_request.h b/skelton/include/http_request.h new file mode 100644 index 0000000..7fdbef8 --- /dev/null +++ b/skelton/include/http_request.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_HTTP_REQUEST +#define _HEADER_HTTP_REQUEST + +#include "types.h" +#include "buf.h" + +class Net_connection_tcp; + +class Http_request { +protected: + static char base64table[64]; + static Byte reversebase64table[256]; + Net_connection_tcp *nc; + Buf buf; + const Byte *request; + int size; + void sendrequest(); + bool sent; +public: + Http_request(const char *host, int port, const Byte *request=NULL, int size=0); + Http_request(Dword hostaddr, int port, const Byte *request=NULL, int size=0); + virtual ~Http_request(); + Byte *getbuf() const; + Dword getsize() const; + Dword getnbrecv() const; + bool isconnected() const; + bool done(); + + Dword gethostaddr() const; + int gethostport() const; + + static void base64encode(const Byte *in, Textbuf& out, Dword size); + static void base64decode(const char *in, Buf& out, Dword size); + + static void url_encode(const char *src, Textbuf& dest); +}; + +#endif diff --git a/skelton/include/id.h b/skelton/include/id.h new file mode 100644 index 0000000..d20b20e --- /dev/null +++ b/skelton/include/id.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_ID +#define _HEADER_ID + +#include "types.h" + +class Identifyable { + static Dword next_id; +protected: + Dword the_id; +public: + Identifyable() { + the_id=next_id++; + } + virtual void set_id(Dword id) { + the_id=id; + } + virtual Dword id() { + return the_id; + } +}; + +#endif diff --git a/skelton/include/image.h b/skelton/include/image.h new file mode 100644 index 0000000..c667bbb --- /dev/null +++ b/skelton/include/image.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_IMAGE +#define _HEADER_IMAGE + +#include "types.h" + +class Image { + public: + virtual int width() const=0; + virtual int height() const=0; + virtual Byte* pic() const=0; + virtual Byte* pal() const=0; + virtual int palettesize() const=0; +}; + +#endif diff --git a/skelton/include/input.h b/skelton/include/input.h new file mode 100644 index 0000000..6d23ed8 --- /dev/null +++ b/skelton/include/input.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_INPUT +#define _HEADER_INPUT + +#include "types.h" +#include "input_keys.h" + +#define SHIFT 1 +#define ALT 2 +#define CONTROL 4 + +#define MAXKEY 32 + +extern const char *keynames[256]; + +class Input { +public: + struct { + int dx,dy,dz; + Byte button[4]; + int quel; + } mouse; + struct { + bool special; + char c; + } key_buf[MAXKEY]; + Byte keys[256]; + bool pause; + int quel_key; + int shift_key; + int key_pending; + static Input* New(bool dumb=false); + virtual ~Input() { }; + virtual void clear_key() = 0; + virtual void check() = 0; + virtual void deraw() = 0; + virtual void reraw() = 0; +}; + +extern Input* input; + +#endif diff --git a/skelton/include/input_dumb.h b/skelton/include/input_dumb.h new file mode 100644 index 0000000..15a3c49 --- /dev/null +++ b/skelton/include/input_dumb.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_INPUT_DUMB +#define _HEADER_INPUT_DUMB + +#include "input.h" + +class Input_Dumb: public Input { +public: + Input_Dumb(); + virtual ~Input_Dumb(); + void process_mouse(); + void process_key(); + void add_key_buf(char c, bool special=false); + virtual void clear_key(); + virtual void check() { + process_key(); + process_mouse(); + } + virtual void deraw() { }; + virtual void reraw() { }; + void restore(); +}; + +#endif /* _HEADER_INPUT_DUMB */ diff --git a/skelton/include/input_dx.h b/skelton/include/input_dx.h new file mode 100644 index 0000000..3b7814f --- /dev/null +++ b/skelton/include/input_dx.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_INPUT_DX +#define _HEADER_INPUT_DX + +#include "input.h" + +class Input_DX: public Input { +public: + LPDIRECTINPUT lpinput; + LPDIRECTINPUTDEVICE lpinputdevice; + LPDIRECTINPUTDEVICE lpinputmouse; + HANDLE mouse_handle; + Input_DX(); + virtual ~Input_DX(); + void process_mouse(); + void process_key(); + void add_key_buf(char c, bool special=false); + virtual void clear_key(); + virtual void check() { + process_key(); + process_mouse(); + } + virtual void deraw() { }; + virtual void reraw() { }; + void restore(); +}; + +#endif /* _HEADER_INPUT_DX */ diff --git a/skelton/include/input_keys.h b/skelton/include/input_keys.h new file mode 100644 index 0000000..bde1c7d --- /dev/null +++ b/skelton/include/input_keys.h @@ -0,0 +1,114 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_INPUT_KEYS +#define _HEADER_INPUT_KEYS + +#define PRESSED 1 +#define RELEASED 2 + +#if defined(UGS_DIRECTX) + +#undef DIRECTINPUT_VERSION +#define DIRECTINPUT_VERSION 0x0300 +#include + +#define KEY_PADENTER DIK_NUMPADENTER +#define KEY_LEFTARROW DIK_LEFTARROW +#define KEY_RIGHTARROW DIK_RIGHTARROW +#define KEY_UPARROW DIK_UPARROW +#define KEY_DOWNARROW DIK_DOWNARROW +#define KEY_LCTRL DIK_LCONTROL +#define KEY_RCTRL DIK_RCONTROL +#define KEY_LSHIFT DIK_LSHIFT +#define KEY_RSHIFT DIK_RSHIFT +#define KEY_LALT DIK_LALT +#define KEY_RALT DIK_RALT +#define KEY_ESCAPE DIK_ESCAPE +#define KEY_ENTER DIK_RETURN +#define KEY_SPACE DIK_SPACE +#define KEY_TAB DIK_TAB +#define KEY_F1 DIK_F1 +#define KEY_F2 DIK_F2 +#define KEY_F3 DIK_F3 +#define KEY_F4 DIK_F4 +#define KEY_F5 DIK_F5 +#define KEY_F6 DIK_F6 +#define KEY_F7 DIK_F7 +#define KEY_F8 DIK_F8 +#define KEY_F9 DIK_F9 +#define KEY_F10 DIK_F10 +#define KEY_F11 DIK_F11 +#define KEY_F12 DIK_F12 + +#elif defined(UGS_LINUX) + +#if 0 +#include + +#define KEY_PADENTER SCANCODE_KEYPADENTER +#define KEY_LEFTARROW SCANCODE_CURSORBLOCKLEFT +#define KEY_RIGHTARROW SCANCODE_CURSORBLOCKRIGHT +#define KEY_UPARROW SCANCODE_CURSORBLOCKUP +#define KEY_DOWNARROW SCANCODE_CURSORBLOCKDOWN +#define KEY_LCTRL SCANCODE_LEFTCONTROL +#define KEY_RCTRL SCANCODE_RIGHTCONTROL +#define KEY_LSHIFT SCANCODE_LEFTSHIFT +#define KEY_RSHIFT SCANCODE_RIGHTSHIFT +#define KEY_LALT SCANCODE_LEFTALT +#define KEY_RALT SCANCODE_RIGHTALT +#define KEY_ESCAPE SCANCODE_ESCAPE +#define KEY_ENTER SCANCODE_ENTER +#define KEY_SPACE SCANCODE_SPACE +#define KEY_TAB SCANCODE_TAB +#define KEY_F1 SCANCODE_F1 +#define KEY_F2 SCANCODE_F2 +#define KEY_F3 SCANCODE_F3 +#define KEY_F4 SCANCODE_F4 +#define KEY_F5 SCANCODE_F5 +#define KEY_F6 SCANCODE_F6 +#define KEY_F7 SCANCODE_F7 +#define KEY_F8 SCANCODE_F8 +#define KEY_F9 SCANCODE_F9 +#define KEY_F10 SCANCODE_F10 +#define KEY_F11 SCANCODE_F11 +#define KEY_F12 SCANCODE_F12 + +#else + +#define KEY_PADENTER 96 +#define KEY_LEFTARROW 105 +#define KEY_RIGHTARROW 106 +#define KEY_UPARROW 103 +#define KEY_DOWNARROW 108 +#define KEY_LCTRL 29 +#define KEY_RCTRL 97 +#define KEY_LSHIFT 42 +#define KEY_RSHIFT 54 +#define KEY_LALT 56 +#define KEY_RALT 100 +#define KEY_ESCAPE 1 +#define KEY_ENTER 28 +#define KEY_SPACE 57 +#define KEY_TAB 15 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_F11 87 +#define KEY_F12 88 + +#endif + +#endif + +#endif /* _HEADER_INPUT_KEYS */ diff --git a/skelton/include/input_svga.h b/skelton/include/input_svga.h new file mode 100644 index 0000000..6ee0c81 --- /dev/null +++ b/skelton/include/input_svga.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_INPUT_SVGA +#define _HEADER_INPUT_SVGA + +#include +#include +#include "input.h" + +class Input_Svgalib: public Input { +private: + bool israw; + bool mouse_reinit; + struct sigaction oldsignals; + struct termios termattr; + int tty_fd; + void process_key(); + void process_mouse(); + void restore_mouse(); + static void mouse_handler(int button, + int dx, int dy, int dz, + int drx, int dry, int drz); + static void keyboard_handler(int scancode, int press); + static void signal_handler(int signal); +public: + Input_Svgalib(); + virtual ~Input_Svgalib(); + virtual void clear_key(); + virtual void check(); + virtual void deraw(); + virtual void reraw(); +}; + +#endif /* _HEADER_INPUT_SVGA */ diff --git a/skelton/include/input_x11.h b/skelton/include/input_x11.h new file mode 100644 index 0000000..e3bcd18 --- /dev/null +++ b/skelton/include/input_x11.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_INPUT_X11 +#define _HEADER_INPUT_X11 + +/* forward declaration un peu laide */ +union _XEvent; +typedef union _XEvent XEvent; +typedef struct _XIM *XIM; +typedef struct _XIC *XIC; + +#include "input.h" + +class Input_X11: public Input { +private: + bool israw; + XIM im; + XIC ic; + void process_key(XEvent event); + void process_mouse(XEvent event); +public: + Input_X11(); + virtual ~Input_X11(); + virtual void clear_key(); + virtual void check(); + virtual void deraw(); + virtual void reraw(); +}; + +#endif /* _HEADER_INPUT_X11 */ diff --git a/skelton/include/inter.h b/skelton/include/inter.h new file mode 100644 index 0000000..e828845 --- /dev/null +++ b/skelton/include/inter.h @@ -0,0 +1,324 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef HEADER_INTER +#define HEADER_INTER +#include "error.h" +#include "array.h" +#include "types.h" +#include "video.h" +#include "bitmap.h" +#include "sprite.h" + +class Zone; +class Inter; + +class Inter { + Zone* in; + int first_zone; + + static int last_mouse_x, last_mouse_y; + static bool kb_visible; + int kb_x, kb_y, kb_anim; + bool kb_active; + int double_click_delay; + Zone *double_clicked_first; + Array kb_keys; + Zone *kb_focus; + Zone *kb_find_upmost(); + Zone *kb_find_downmost(); + Zone *kb_find_down(); + Zone *kb_find_up(); + Zone *kb_find_left(); + Zone *kb_find_right(); + Zone *kb_find_closest(); + Zone *kb_find_next(); + Zone *kb_find_prev(); + void kb_draw_focus(); + void de_tag(Zone *z); + void tag(Zone *z); + bool kb_check_key(const int i) const; +public: + Font* font; + bool del_font; + Array zone; + Zone* focus; + Zone* clicked, *double_clicked; + Inter(); + Inter(Inter *in); + void set_font(Font* f1, bool del=true); // del=true if we must delete 'font' + int nzone() const { + return zone.size(); + } + void add(Zone* zon, bool back=false) { + if(back) + zone.add_before(zon, 0); + else + zone.add(zon); + } + void remove(int i); + void remove(Zone *z); + Zone* do_frame(); + void dirt_all(); + void draw_zone(); + void flush(); + virtual ~Inter(); + void process(); + void select_zone(Zone *z, int quel); // selectionne une zone (donne focus et/ou click) + bool is_kb_visible() const { + return kb_visible; + } + void kb_deactivate(); + void kb_reactivate(); + void kb_alloc_key(const int i); + void kb_free_key(const int i); +}; + +class Zone { + TRACKED; +public: + Zone* child,* parent; + int enabled; + int dirty, focusable; + int x, y, w, h; + bool kb_focusable, stay_on_top; + Inter* inter; + Zone(Inter* in=NULL, int px=0, int py=0, int pw=0, int ph=0); + virtual ~Zone(); + virtual void disable() { + if(enabled == 0) + video->need_paint = 2; // to ensure that this zone will disappear + enabled--; + } + virtual void enable() { + enabled++; + } + virtual void dirt() { + dirty = 2; + } + virtual void draw() { } //called when the zone is dirty + virtual int in() const; + virtual void leaved(); + virtual void entered(); + virtual void waiting(); + virtual void clicked(int quel); + virtual void double_clicked(); + virtual void process() { } + virtual void lost_focus(int cancel) { } + void set_child(Zone* chil); +}; + +class Zone_sprite: public Zone { + Sprite *sp; +public: + Zone_sprite(Inter *in, const char *nam, int px=-1, int py=-1); + virtual ~Zone_sprite(); + virtual void draw(); +}; + +class Zone_bitmap: public Zone { + bool del_bit; +public: + Bitmap* bit_, *bit2_, *actual; + Zone_bitmap(Inter* in, Bitmap* bit, int px, int py, Bitmap* bit2=NULL); + Zone_bitmap(Inter* in, Bitmap* bit, int px, int py, bool del); + virtual ~Zone_bitmap(); + virtual void draw() { + actual->draw(video->vb, x, y); + } + virtual void leaved() { + if(bit2_) { + actual = bit_; + dirt(); + } + Zone::leaved(); + } + virtual void entered() { + if(bit2_) { + actual = bit2_; + dirt(); + } + Zone::entered(); + } +}; + +class Zone_watch_int: public Zone { +public: + int *val, last_val; + Zone_watch_int(Inter* in, int *pval, int px=0, int py=0, int pw=0, int ph=0): + Zone(in, px, py, pw, ph) { + val = pval; + set_val(pval); + } + virtual void process(); + void set_val(int *pv); +}; + +class Zone_state: public Zone_watch_int { +public: + int nstate; + Zone_state(Inter* in, int *pval, int px, int py, int pw=0, int ph=0, int pnstate=1); + virtual void clicked(int quel); +}; + +class Zone_state_bit: public Zone_state { + Bitmap *state[3]; +public: + Zone_state_bit(Inter* in, const char* b1, int *pval, int px, int py, const char* b2=NULL, const char* b3=NULL); + virtual ~Zone_state_bit() { + for(int i=0; i < nstate; i++) + delete state[i]; + } + virtual void draw() { + state[last_val]->draw(video->vb, x, y); + } +}; + +class Zone_text: public Zone { + friend class Zone_text_select; +protected: + Font *font; + bool lock_size; + int text_x; +public: + char st[256]; + Zone_text(Inter* in, const char* s, int px, int py); + Zone_text(Font *f2, Inter* in, const char* s, int px, int py); + Zone_text(Font *f2, Inter* in, const char* s, int py); + Zone_text(Inter* in, const char* s, int px, int py, int pw); + Zone_text(Inter* in, const char* s, int py); + virtual void set_text(const char* s); + virtual void set_font(Font* f); + virtual void draw(); +}; + +class Zone_text_select: public Zone_text { +protected: + Font* font2; + Font* actual; +public: + Zone_text_select(Inter* in, Font* f, const char* s, int px, int py); + Zone_text_select(Inter* in, Font* f, const char* s, int px, int py, int pw); + virtual void draw(); + virtual void leaved(); + virtual void entered(); + virtual void set_font(Font* f); +}; + +class Zone_text_button: public Zone_text_select { +protected: + Bitmap *bit; + bool high; + void set_bit(Bitmap *fond); +public: + Zone_text_button(Inter* in, Bitmap* fond, Font* f, const char* s, int px, int py); + Zone_text_button(Inter* in, Bitmap* fond, Font* f, const char* s, int py); + Zone_text_button(Inter* in, Bitmap* fond, Font* f, const char* s, int px, int py, int pw); + virtual ~Zone_text_button(); + virtual void draw(); + virtual void leaved(); + virtual void entered(); + virtual void set_text(const char* s); +}; + +class Zone_clear: public Zone { + int color; +public: + Zone_clear(Inter* in, int px=0, int py=0, int pw=video->width, int ph=video->height, int c=0); + virtual void draw(); +}; + +class Zone_panel: public Zone { +protected: + bool draw_frame; +public: + Video_bitmap* pan; + bool high; + Zone_panel(Inter* in, int px, int py, int pw, int ph); + virtual ~Zone_panel() { + delete pan; + } + virtual void draw(); + void resize(); +}; + +class Zone_state_text: public Zone_state { + Zone_panel* pan; +public: + const char* state[256]; + Font* fonts[256]; + Zone_state_text(Inter* in, int *pval, int px, int py, int pw=50, int ph=20); + virtual ~Zone_state_text() { + delete pan; + } + void add_string(const char* s, Font *f=NULL); + virtual void draw(); + virtual void leaved(); + virtual void entered(); +}; + +class Zone_text_input: public Zone_panel { +protected: + char st[1024]; + char* val; + int focus, curpos, actual_len, maxlen, panx, maxwidth; + void input_char(const Byte c); + void check_clipboard(); + Font *font_selected; + int select_start; + Byte curcolor; + bool first_click; + void set_mouse_curpos(); + bool cut_selection(); +public: + Zone_text_input(Inter* in, const Palette& pal, char* s, int mlen, int px, int py, int pw, int mwidth=-1); + virtual ~Zone_text_input(); + virtual void clicked(int quel); + virtual void lost_focus(int cancel); + void set_val(char* s); + virtual void draw(); + virtual void process(); + virtual void leaved(); + virtual void entered(); + virtual void waiting(); +}; + +class Zone_input_numeric: public Zone_text_input { + char temp_st[64]; + int var_min, var_max, *num_var; +public: + Zone_input_numeric(Inter* in, int *pvar, int ncar, int pmin, int pmax, const Palette &pal, int px, int py, int pw); + void lost_focus(int cancel); +}; + +class Zone_text_field: public Zone_panel { +protected: + char st[256]; + int val; + int *var; + Font *font; +public: + Zone_text_field(Inter* in, int* s, int px, int py, int pw, Font *f2=NULL, bool frame=true); + Zone_text_field(Inter* in, const char* s, int px, int py, int pw, Font *f2=NULL, bool frame=false); + void set_val(int* s); + void set_val(const char* s); + virtual void draw(); + virtual void process(); +}; + +class Zone_text_numeric: public Zone_text_field { +public: + Zone_text_numeric(Inter* in, int* s, int px, int py, int pw): + Zone_text_field(in, s, px, py, pw) { + draw_frame = false; + } + Zone_text_numeric(Font *f2, Inter* in, int* s, int px, int py, int pw): + Zone_text_field(in, s, px, py, pw, f2) { + draw_frame = false; + } +}; + +#endif diff --git a/skelton/include/listbox.h b/skelton/include/listbox.h new file mode 100644 index 0000000..9879a42 --- /dev/null +++ b/skelton/include/listbox.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef HEADER_LISTBOX +#define HEADER_LISTBOX +#include "types.h" +#include "error.h" +#include "inter.h" + +class Zone_listbox; + +class Zone_listupdown: public Zone_text_select { + friend class Zone_listup; + friend class Zone_listdown; + Zone_listbox *parent; + int count; +public: + Zone_listupdown(Zone_listbox *par, const char *s, int py); + virtual void waiting(); + virtual void leaved(); + virtual void dirt(); +}; + +class Zone_listup: public Zone_listupdown { +public: + Zone_listup(Zone_listbox *par); + virtual void clicked(int quel); +}; + +class Zone_listdown: public Zone_listupdown { +public: + Zone_listdown(Zone_listbox *par); + virtual void clicked(int quel); +}; + +class Listable { +public: + char list_name[64]; + Font *font; + Listable(const char *s, Font *f=NULL); + virtual ~Listable() { + } + virtual bool is_equal(Listable *source); +}; + +class Zone_listtext: public Zone_text { + Zone_listbox *parent; + int quel; + bool high; +public: + Zone_listtext(Zone_listbox *par, int i); + virtual void clicked(int quel); + virtual void draw(); + virtual void dirt(); + virtual void entered(); + virtual void leaved(); +}; + +class Zone_listbox: public Zone_watch_int { + friend class Zone_listupdown; + friend class Zone_listup; + friend class Zone_listdown; + friend class Zone_listtext; + Bitmap *back; + Zone_listup *zup; + Zone_listdown *zdown; + Font *font2; + Array elements; // liste des elements de la list_box + int first_item; // premier item afficher dans la list_box + Array list; // liste des zone_text affichées + Array sort_list; // liste temporaire d'element a sorter + static int compare_sort(const void *arg1, const void *arg2); + Video_bitmap *screen; + bool selectable; +public: + Zone_listbox(Inter* in, Bitmap *fond, Font *f, int *pval, int px, int py, int pw, int ph); + virtual ~Zone_listbox(); + virtual void draw(); + virtual void dirt(); + virtual void enable(); + virtual void disable(); + virtual void process(); + void add_item(const char *s) { + add_item(new Listable(s)); + } + void add_item(Listable *e); + void replace_item(int i, Listable *e); + void remove_item(Listable *e); + void remove_item(int i); + Listable *get_selected(); + void clear(); + void sync_list(); + void unselect(); + void empty(); + void select(int q); + int search(Listable *source); + bool in_listbox(const Zone *z); // demande si 'z' est un element de la listbox + void init_sort(); + void add_sort(Listable *l); + void end_sort(); +}; + +#endif diff --git a/skelton/include/main.h b/skelton/include/main.h new file mode 100644 index 0000000..bfe6134 --- /dev/null +++ b/skelton/include/main.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MAIN +#define _HEADER_MAIN + +#include "types.h" + +#ifdef UGS_DIRECTX + #define WIN32_LEAN_AND_MEAN + #include + #include + extern HINSTANCE hinst; + extern HWND hwnd; +#endif + +enum Time_mode { + TIME_NORMAL, + TIME_FREEZE, + TIME_SLOW, + TIME_FAST +}; + +extern Time_mode time_control; + +extern bool alt_tab; + +extern char exe_directory[]; + +void start_game(); +void quit_game(); +void start_frame(); +void end_frame(); +Dword getmsec(); + +#endif diff --git a/skelton/include/music.h b/skelton/include/music.h new file mode 100644 index 0000000..9c397ad --- /dev/null +++ b/skelton/include/music.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_MUSIC +#define _HEADER_MUSIC + +class Music { +public: + bool active; + static Music* alloc(); + virtual ~Music() { }; + virtual void open() = 0; + virtual void close() = 0; + virtual void play(int quel, bool loop=false) = 0; + virtual void replay() = 0; + virtual void stop() = 0; +}; + +extern Music *music; + +#endif diff --git a/skelton/include/net.h b/skelton/include/net.h new file mode 100644 index 0000000..8721b40 --- /dev/null +++ b/skelton/include/net.h @@ -0,0 +1,295 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NET +#define _HEADER_NET + +#ifdef UGS_DIRECTX +#include "winsock.h" +#endif + +#ifdef UGS_LINUX +#include +#ifndef __STRICT_ANSI__ +#define I_SET_STRICT_ANSI +#define __STRICT_ANSI__ +#endif +#include +#ifdef I_SET_STRICT_ANSI +#undef I_SET_STRICT_ANSI +#undef __STRICT_ANSI__ +#endif +#include +#include +#define SOCKET_ERROR -1 +#endif + +#include "array.h" +#include "types.h" +#include "packet.h" +#include "notify.h" +#include "net_call.h" +#include "id.h" +#include "buf.h" + +class IP_addr { +public: + Dword ip, mask; + IP_addr(const IP_addr& o); + IP_addr(const char *addr); + IP_addr(Dword pip, Dword pmask=0xFFFFFFFF); + void print(char *st); + bool operator>(const IP_addr& o); + bool operator>=(const IP_addr& o); + void set(const char *addr); + void set(Dword pip, Dword pmask); +}; + +class Net_buf; +class Net_connection; +class Net_connection_tcp; + +class Packet; + +class Net_param { +public: + virtual bool accept_connection(Net_connection *nc); + virtual void server_deconnect() { } // handle event in case of server disconnection + virtual void client_connect(Net_connection *adr) { } + virtual void client_deconnect(Net_connection *adr) { } // handle event in case of a client disconnection (on server only) + virtual int tcpport()=0; + virtual int udpport() { + return tcpport()+1; + } + virtual void print_packet(Packet *p2, char *st)=0; + virtual Packet *alloc_packet(Word pt)=0; + virtual bool is_dispatchable(Net_connection *nc, Packet *p) { + return true; + } + virtual Dword magic()=0; + virtual char *get_motd() { + return "Hello!"; + } +}; + +class Exec_ping: public Net_callable { + static Dword next_uid; + Net_connection *dest; + Word type; + Dword uid; + Net_callable *net_callable; +public: + //default d to 0 for clients + Exec_ping(Packet_ping *p, Word pt, Net_callable *netc, Net_connection *d=NULL); + virtual ~Exec_ping(); + virtual void net_call(Packet *p2); + virtual bool validate(Packet_ping *p2); +}; + +class Buf; + +class Net_connection: public Identifyable { +public: + enum Netstate { + invalid, valid, waitingfordns, dnslookup, connecting, connected, disconnected + }; + bool packet_based; + bool joined; + bool trusted; + Dword incoming_inactive; + Dword outgoing_inactive; + Dword outgoing_size; + Dword outgoing_min; + Dword outgoing_max; + Dword outgoing_total; + Dword incoming_size; + Dword incoming_min; + Dword incoming_max; + Dword incoming_total; + Dword commit_count_in; + Dword commit_count_out; + Dword commit_count_both; + Dword commit_count_total; + Buf *incoming; //Set only when !packet_based + virtual Netstate state(); + virtual void connect(Net_connection *dest); + virtual void connect(Dword adr, int port); + virtual void connect(const char* host, int port); + virtual bool checktcp(); + virtual void receivetcp(Net_buf *p); + virtual int receivetcp(Byte *buf, Dword size); + virtual void sendtcp(Packet *p2); + virtual void sendtcp(const Byte *buf, Dword size); + virtual void commit(); + virtual void disconnect(); + virtual Dword address() const { + return INADDR_LOOPBACK; + } + virtual Dword getdestaddr() const { + return INADDR_LOOPBACK; + } + virtual int getdestport() const { + return destport; + } + virtual Dword getbufsize() const; + virtual int getFD() const { + return -1; + } + Net_connection(); + virtual ~Net_connection(); +protected: + int destport; + Netstate _state; +private: + static int next_port; + Buf *buf; + Net_connection *connected_to; +}; + +class Net_connection_tcp: public Net_connection { +private: + friend class Net; + int tcpsock; + Dword from; + sockaddr_in tcpsin; + Byte tcpbuf[1024]; + Dword tcpbufsize; + Dword tcppacsize; + char desthost[256]; + Dword destaddr; + int destport; + Buf outgoing_buf; +public: + Net_connection_tcp(int p=0, bool ppacket_based=true); + Net_connection_tcp(int sock, Dword adr, int port, bool ppacket_based=true); + virtual ~Net_connection_tcp(); + virtual Netstate state(); + virtual void connect(Dword adr, int port); + virtual void connect(const char* host, int port); + virtual bool checktcp(); + virtual void receivetcp(Net_buf *p); + virtual int receivetcp(Byte *buf, Dword size); + virtual void sendtcp(Packet *p); + virtual void sendtcp(const Byte *buf, Dword size); + virtual void commit(); + virtual Dword address() const { + return from; + } + virtual Dword getdestaddr() const { + return destaddr; + } + virtual int getdestport() const { + return destport; + } + virtual Dword getbufsize() const { + return tcpbufsize; + } + virtual int getFD() const; +}; + +class Http_request; + +class Net: public Observable { +public: + Array connections; + bool active; + char *last_error; + Dword name_resolve; + int port_resolve; + char host_name[1024]; + Array host_adr; + + Net_param *net_param; + + Net(Net_param *np); + virtual ~Net(); + + //Client+Serveur + Net_connection *server_addr(); + void step(bool loop_only=false); + void addwatch(Word id, Net_callable *nc); + void removewatch(Word id, Net_callable *nc); + void sendudp(Dword to, Packet *p); + + //Server + void start_server(bool sock); + void stop_server(); + void suspend_server(); + void sendtcp(Net_connection *nc, Packet *p); + void dispatch(Packet *p, Dword pt, Net_connection *nc=NULL); + + //Client + Net_connection *start_loopback_client(); + void start_client(Dword adr, int port=0); + void stop_client(); + bool connected(); + void sendtcp(Packet *p); + + //All-purpose as long as TCP/IP is there :) + Net_connection_tcp *start_other(Dword adr, int port); + Net_connection_tcp *start_other(const char *host, int port); + + //Utils + Packet *net_buf2packet(Net_buf *nb, bool tcp); + + static Dword dotted2addr(const char *host); + static void stringaddress(char *st, Dword adr); + static void stringaddress(char *st, Dword adr, int port); + + Dword getaddress(const char *host); + void gethostbyname_completed(bool success); + void gethostbyname_cancel(); + char *failed(); + void init_local_addresses(); + void init_all_udp(); + void close_all_udp(); +private: + friend class Net_connection_tcp; + friend class Net_connection; + Net_connection *server_connection; + Net_connection *client_connection; + + friend class Packet_udp; + + class Net_receive_cb { + public: + Word id; + Net_callable *net_callable; + Net_receive_cb(Word i, Net_callable *nc) { + id = i; + net_callable = nc; + } + }; + Array callbacks; + + int udpsock[32], udpport, udpnum; + sockaddr_in udpsin; + + void packetreceived(Net_buf *nb, bool tcp); + + int checkreceive(int s); + void receiveudp(int sock, Net_buf *p); + bool accept(); + int open_udpsock(Dword adr); + void verify_connections(); + void verify_server_connection(); + + /* true si erreur (message dans last_error) */ + bool checkerror(int quel); + /* verifie si erreur etendu dans WSALasterror ou errno */ + bool getlasterror(int quel); + /* quit avec message d'erreur */ + void callwsa(int quel); + + #ifdef UGS_DIRECTX + char name_buf[MAXGETHOSTSTRUCT]; + HANDLE name_handle; + #endif +}; + +extern Net* net; + +#endif diff --git a/skelton/include/net_buf.h b/skelton/include/net_buf.h new file mode 100644 index 0000000..ae82b46 --- /dev/null +++ b/skelton/include/net_buf.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NET_BUF +#define _HEADER_NET_BUF + +#include "types.h" +#include "net.h" + +class Net_buf { +public: + Byte *point; + Net_connection *from; + Dword from_addr; + Byte buf[1024]; + void write_dword(Dword v) { + *(Dword *) point = htonl(v); + point += sizeof(Dword); + } + void write_word(Word v) { + *(Word *) point = htons(v); + point += sizeof(Word); + } + void write_byte(Byte v) { + *(Byte *) point = v; + point += sizeof(Byte); + } + void write_bool(bool b) { + write_byte(b? 1:0); + } + void write_mem(void *v, int num) { + memcpy(point, v, num); + point += num; + } + void write_string(char *v) { + write_mem(v, strlen(v)+1); // ecrit un string avec son '0' + } + Dword read_dword() { + if(((unsigned int)len())<=1024-sizeof(Dword)) { + Dword ret = ntohl(*(Dword *) point); + point += sizeof(Dword); + return ret; + } + else + return 0; + } + Word read_word() { + if(((unsigned int)len())<=1024-sizeof(Word)) { + Word ret = ntohs(*(Word *) point); + point += sizeof(Word); + return ret; + } + else + return 0; + } + Byte read_byte() { + if(((unsigned int)len())<=1024-sizeof(Byte)) { + Byte ret = *(Byte *) point; + point += sizeof(Byte); + return ret; + } + else + return 0; + } + bool read_bool() { + if(read_byte()) + return true; + else + return false; + } + void read_mem(void *v, int num) { + if(len()<=1024-num) { + memcpy(v, point, num); + point += num; + } + else + memset(v, 0, num); + } + bool read_string(char *v, int size) { // lit un string avec son '0' + do { + char c=(char)read_byte(); + if(c>0 && c<' ') + c=' '; + *v=c; + if(!*v) + return true; + v++; + size--; + } while(size); + if(v[-1]) { + v[-1]=0; + return false; + } + return true; + } + Net_buf() { + reset(); + from=NULL; + memset(buf, 0, sizeof(buf)); + } + void reset() { + point = buf; + } + const int len() const { + return point - buf; + } +}; + +#endif diff --git a/skelton/include/net_call.h b/skelton/include/net_call.h new file mode 100644 index 0000000..c131932 --- /dev/null +++ b/skelton/include/net_call.h @@ -0,0 +1,17 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NET_CALL +#define _HEADER_NET_CALL + +class Packet; + +class Net_callable { +public: + virtual void net_call(Packet *p)=0; +}; + +#endif diff --git a/skelton/include/notify.h b/skelton/include/notify.h new file mode 100644 index 0000000..d5673e0 --- /dev/null +++ b/skelton/include/notify.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_NOTIFY +#define _HEADER_NOTIFY +#include "array.h" + +class Notifyable { +public: + virtual void notify()=0; +}; + +class Observable { + Array notes; +public: + virtual ~Observable(); + virtual void add_watch(Notifyable *n); + virtual void remove_watch(Notifyable *n); + virtual void notify_all(); +}; + +#endif diff --git a/skelton/include/overmind.h b/skelton/include/overmind.h new file mode 100644 index 0000000..c576c45 --- /dev/null +++ b/skelton/include/overmind.h @@ -0,0 +1,108 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_OVERMIND +#define _HEADER_OVERMIND + +#include "track.h" +#include "array.h" +#include "types.h" +#include "inter.h" + +class Module; +extern Inter* ecran; + +class Executor { + friend class Module; + friend class Overmind; + bool paused, self_destruct; +protected: + Array modules; +public: + bool done; + Executor(bool self_des=false); + virtual ~Executor(); + void add(Module* m); + void remove(); + void pause() { + paused = true; + } + void unpause() { + paused = false; + } + virtual void step(); +}; + +class Module { + TRACKED; + friend class Executor; +protected: + Executor* parent; + bool first_time; + bool done; +public: + Module(); + virtual ~Module(); + + //will be called repeatedly by the executor until 'ret' is called + virtual void step(); + + //will be called once by the executor to initialise + virtual void init(); + + //'this' will be deleted by the executor + //'module' will replace it and start executing + void exec(Module* module); + + //'this' will be put on hold + //'module' will start execution + //'this' will resume after 'module' calls 'ret' + void call(Module* module); + + //'this' will be deleted by the executor + //the caller will resume execution + //if there is no caller, the executor will terminate + void ret(); +}; + +class Module_thread: public Module { +public: + Module_thread(); +}; + +class Overmind { +protected: + Array execs; + bool paused; +public: + bool done; + Dword framecount; + Overmind(); + virtual ~Overmind(); + void step(); + void start(Executor* e); + void stop(Executor* e); + void pause(); + void unpause(); + void clean_up(); +}; + +class Menu: public Module { + Inter* old_ecran; +protected: + Inter* inter; + Zone* result; +public: + Palette pal; + Menu(Inter* base=NULL); + virtual ~Menu(); + virtual void init(); + virtual void step(); +}; + +extern Overmind overmind; + +#endif diff --git a/skelton/include/packet.h b/skelton/include/packet.h new file mode 100644 index 0000000..4a37137 --- /dev/null +++ b/skelton/include/packet.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PACKET +#define _HEADER_PACKET + +#include "track.h" +#include "types.h" + +class Net_buf; +class Net_connection; + +class Packet { + TRACKED; +public: + bool istcp; + Net_connection *from; + Dword from_addr; + Byte packet_id; + Packet(); + virtual ~Packet() { }; + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); +}; + +class Packet_tcp: public Packet { +public: + Packet_tcp(); + virtual bool read(Net_buf *p); +}; + +class Packet_udp: public Packet { +public: + Dword magic; + Packet_udp(); + virtual bool read(Net_buf *p); + virtual void write(Net_buf *p); +}; + +class Packet_ping: public Packet_tcp { +public: + Dword uid; + virtual void write(Net_buf *p); + virtual bool read(Net_buf *p); + void answer(Packet_ping *p2); +}; + +#endif diff --git a/skelton/include/palette.h b/skelton/include/palette.h new file mode 100644 index 0000000..2e76745 --- /dev/null +++ b/skelton/include/palette.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PALETTE +#define _HEADER_PALETTE + +#ifdef UGS_DIRECTX +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#include "types.h" +#include "error.h" +#include "utils.h" +#include "raw.h" + +#ifndef UGS_DIRECTX +typedef struct { + Byte peRed, peGreen, peBlue, peFlags; +} PALETTEENTRY; +#endif + +class Palette { +private: + friend class Fade; + friend class Remap; + PALETTEENTRY pal[256]; +public: + int size; + Palette() { + mset(pal, 0, sizeof(pal)); + size=256; + } + Palette(const Image& raw) { + mset(pal, 0, sizeof(pal)); + load(raw); + } + void set_size(int s) { + size=s; + } + void load(const Image& raw); + void set(); + Byte r(Byte c) { + return pal[c].peRed; + } + Byte g(Byte c) { + return pal[c].peGreen; + } + Byte b(Byte c) { + return pal[c].peBlue; + } + void setcolor(Byte c, Byte r, Byte g, Byte b) { + pal[c].peRed=r; + pal[c].peGreen=g; + pal[c].peBlue=b; + } +}; + +extern Palette noir; + +class Remap { + const Palette& dst; +public: + Byte map[256]; + Remap(const Palette& d, Palette* src=NULL); + void findrgb(const Byte m, const Byte r, const Byte g, const Byte b); +}; + +class Fade { + Palette dest; + short delta[768]; + short current[768]; + int currentframe; + int destframe; +public: + Fade(const Palette& dst=Palette(), const Palette& src=Palette(), int frame=70); + void newdest(const Palette& dst, int frame=70); + void setdest(const Palette& dst); + bool done() const { + return currentframe==destframe; + } + int step(); + void set(); +}; + +#endif diff --git a/skelton/include/pcx.h b/skelton/include/pcx.h new file mode 100644 index 0000000..d145882 --- /dev/null +++ b/skelton/include/pcx.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PCX +#define _HEADER_PCX + +#include "res.h" +#include "image.h" + +#pragma pack(1) +class Pcx: public Image { + struct Head { + Byte manufacturer; + Byte version; + Byte encoding; + Byte bpp; + Word x1,y1,x2,y2; + Word hdpi, vdpi; + Byte colormap[48]; + Byte reserved; + Byte nplane; + Word byteperline; + Word paletteinfo; + Word screensizeh, screensizev; + Byte filler[54]; + } h; + int width_, height_; + Byte* pic_; + Byte* pal_; + public: + Pcx(Res& res); + virtual ~Pcx(); + int width() const { return width_; } + int height() const { return height_; } + Byte* pic() const { return pic_; } + Byte* pal() const { return pal_; } + int palettesize() const { return 256; } +}; +#pragma pack() + +#endif diff --git a/skelton/include/pcx24.h b/skelton/include/pcx24.h new file mode 100644 index 0000000..5d13ab9 --- /dev/null +++ b/skelton/include/pcx24.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_PCX24 +#define _HEADER_PCX24 + +#include "types.h" +class Res; + +#pragma pack(1) +class Pcx24 { + struct Head { + Byte manufacturer; + Byte version; + Byte encoding; + Byte bpp; + Word x1,y1,x2,y2; + Word hdpi, vdpi; + Byte colormap[48]; + Byte reserved; + Byte nplane; + Word byteperline; + Word paletteinfo; + Word screensizeh, screensizev; + Byte filler[54]; + } h; + int width_, height_; + Byte* pic_; + public: + Pcx24(Res& res); + virtual ~Pcx24(); + int width() const { return width_; } + int height() const { return height_; } + Byte* pic() const { return pic_; } +}; +#pragma pack() + +#endif diff --git a/skelton/include/random.h b/skelton/include/random.h new file mode 100644 index 0000000..6cd87f2 --- /dev/null +++ b/skelton/include/random.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RANDOM +#define _HEADER_RANDOM + +#include "types.h" + +class Random { + int seed; +public: + Random(); + Random(int p); + int get_seed() const; + void set_seed(int p); + Word rnd(int and=0xFFFF); + Word crap_rnd(int and=0xFFFF); +}; + +extern Random ugs_random; + +#endif diff --git a/skelton/include/raw.h b/skelton/include/raw.h new file mode 100644 index 0000000..f7c3470 --- /dev/null +++ b/skelton/include/raw.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RAW +#define _HEADER_RAW + +#include "types.h" +#include "res.h" +#include "image.h" + +#pragma pack(1) +class Raw: public Image { + struct Head { + static char signature[6]; + char sig[6]; + Word version; + Word width; + Word height; + Word palettesize; + Word HDPI; + Word VDPI; + Word gamma; + char reserved[12]; + void xlat(); + } h; + Byte* pic_; + Byte* pal_; + public: + Raw(Res& res); + Raw(int w, int h, int ps); + virtual ~Raw(); + int width() const { return h.width; } + int height() const { return h.height; } + Byte* pic() const { return pic_; } + Byte* pal() const { return pal_; } + int palettesize() const { return h.palettesize; } + void write(Res_dos& r); +}; +#pragma pack() + +#endif diff --git a/skelton/include/registry.h b/skelton/include/registry.h new file mode 100644 index 0000000..ef5b78d --- /dev/null +++ b/skelton/include/registry.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_REGISTRY +#define _HEADER_REGISTRY + +class Registry { +public: + static Registry *alloc(); + virtual ~Registry() { }; + virtual void open(const char *n, const char *dir)=0; + virtual void write(const char *k, char *v)=0; + virtual void read(const char *k, char *buffer, unsigned long size)=0; + virtual void close()=0; +}; + +#endif diff --git a/skelton/include/res.h b/skelton/include/res.h new file mode 100644 index 0000000..5446b8c --- /dev/null +++ b/skelton/include/res.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RES +#define _HEADER_RES + +#include +#include +#include "resfile.h" +#include "resmanager.h" + +#ifdef UGS_LINUX + #include +#endif + +#ifdef UGS_DIRECTX + #define WIN32_LEAN_AND_MEAN + #include + #include + #include + #include +#endif + +#include "track.h" +#include "error.h" +#include "types.h" +#include "utils.h" + +class Res { +public: + virtual ~Res() { } + virtual int read(void *b, int nb)=0; + virtual Dword size()=0; + virtual void position(Dword po)=0; + virtual const void *buf()=0; + virtual bool eof()=0; + virtual Dword get_position()=0; +}; + +class Res_mem: public Res { +protected: + Byte *_buf; + Dword pos; +public: + Res_mem(); + virtual int read(void *b, int nb) { + if(pos+nb>size()) { + mset(b, 0, nb); + nb=size()-pos; + } + cpy((Byte *) b, _buf + pos, nb); + pos += nb; + return nb; + } + virtual void position(Dword po) { + pos = po; + } + virtual const void *buf() { + return(_buf + pos); + } + virtual bool eof() { + return (pos >= size()); + } + virtual Dword get_position() { + return pos; + } +}; + +#ifdef ONVEUTDESRESDOZEPOCHES +class Res_doze: public Res_mem { + HRSRC hResInfo; +public: + Res_doze(LPCTSTR lpName); + virtual ~Res_doze() { + FreeResource(hResInfo); + } + virtual Dword size() { + return SizeofResource(NULL, hResInfo); + } +}; +#endif + +class Res_doze: public Res_mem { + unsigned int ressize; +public: + Res_doze(const char *resname) { + ressize = resmanager->get(resname, &_buf); + if(!_buf) + (void) new Error("Unable to find resource: %s", resname); + } + virtual ~Res_doze() { + } + virtual Dword size() { + return ressize; + } +}; + +enum Res_mode { + RES_READ, + RES_WRITE, + RES_CREATE, + RES_TRY +}; + +class Res_dos: public Res { + TRACKED; + int handle; + void *_buf; +public: + bool exist; + Res_dos(const char *fil, Res_mode mode=RES_READ); + virtual ~Res_dos(); + virtual void position(Dword po); + virtual int read(void *b, int nb); + virtual void write(const void *b, int nb); + virtual Dword size(); + virtual const void* buf(); + virtual bool eof(); + virtual Dword get_position(); +}; + +#endif diff --git a/skelton/include/res_compress.h b/skelton/include/res_compress.h new file mode 100644 index 0000000..e46765d --- /dev/null +++ b/skelton/include/res_compress.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RES_COMPRESS +#define _HEADER_RES_COMPRESS + +#include "res.h" + +class Res_compress: public Res_mem { + Res_mode mode; + Res *res; + Res_dos *res_dos; + mutable Dword ressize; + mutable Dword write_pos; + void read_uncompress(); +public: + Byte *write_compress(Dword *size); + bool exist; + Res_compress(const char *fil, Res_mode pmode=RES_READ, bool res_doze=false); + virtual ~Res_compress(); + virtual void write(const void *b, int nb); + virtual Dword size(); +}; + +#endif diff --git a/skelton/include/resfile.h b/skelton/include/resfile.h new file mode 100644 index 0000000..7703e1a --- /dev/null +++ b/skelton/include/resfile.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RESFILE +#define _HEADER_RESFILE + +#include "types.h" + +const char signature[4] = "UGS"; + +class Resdata { +private: + friend class Resfile; + int size; + Byte *data; +public: + char *name; + Resdata *next; + Resdata(char *resname, int ressize, Byte *resdata, Resdata *list); + virtual ~Resdata(); +}; + +class Res_dos; + +class Resfile { +private: + Res_dos *res; +public: + Resdata *list; + Resfile(const char *fname, bool ro=true); + virtual ~Resfile(); + virtual void freeze(); + virtual void thaw(); + virtual void clear(); + virtual void add(const char *resname, int size, const char *resdata); + virtual int get(const char *resname, Byte **resdata); + virtual void remove(const char* resname); +}; + +#endif diff --git a/skelton/include/resmanager.h b/skelton/include/resmanager.h new file mode 100644 index 0000000..1517273 --- /dev/null +++ b/skelton/include/resmanager.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_RESMANAGER +#define _HEADER_RESMANAGER + +#include "types.h" +#include "array.h" + +class Resfile; + +class Resmanager { +private: + Array files; +public: + Resmanager(); + virtual ~Resmanager(); + virtual void loadresfile(const char *fname); + virtual int get(const char *resname, Byte **resdata); +}; + +extern Resmanager *resmanager; + +#endif diff --git a/skelton/include/sound.h b/skelton/include/sound.h new file mode 100644 index 0000000..ec5d8f9 --- /dev/null +++ b/skelton/include/sound.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_SOUND +#define _HEADER_SOUND + +#ifdef UGS_DIRECTX +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#endif + +#include "array.h" +#include "res.h" + +class Sample; +class Sfx; +#ifdef UGS_LINUX +class Playing_sfx; +#endif + +class Sound { + friend class Sample; + friend class Sfx; +#ifdef UGS_DIRECTX + LPDIRECTSOUND lpds; +#endif +#ifdef UGS_LINUX + int dspfd; + unsigned int channels; + unsigned int sampling; + unsigned int bps; + unsigned int fragsize; + void *fragbuf; + Array plays; +#endif +public: + bool active; + Sound(); +#ifdef UGS_LINUX + void process(); + void start(Playing_sfx* play); +#endif + void delete_sample(Sample *sam); + virtual ~Sound(); +}; + +class Sample { + friend class Sfx; +#ifdef UGS_DIRECTX + BYTE *pbWaveData; + DWORD cbWaveSize; + int iAlloc; + int iCurrent; + IDirectSoundBuffer* buffers[64]; + + IDirectSoundBuffer *DSLoadSoundBuffer(void *res); + void DSFillSoundBuffer(IDirectSoundBuffer *pDSB, BYTE *pbWaveData, DWORD cbWaveSize); + void DSParseWaveResource(void *res, WAVEFORMATEX **ppWaveHeader, BYTE **ppbWaveData,DWORD *pcbWaveSize); + //void DSReloadSoundBuffer(IDirectSoundBuffer *pDSB, LPCTSTR lpName); + IDirectSoundBuffer *getfreebuffer(); +#endif +#ifdef UGS_LINUX + void loadriff(const char *res, unsigned int size); + void resample(char* sample, unsigned int size, unsigned int bps); +#endif +public: +#ifdef UGS_LINUX + void *audio_data; + unsigned int sampling; + unsigned int length; +#endif + Sample(Res& re, int nb); + virtual ~Sample(); + void stop(); +}; + +#ifdef UGS_LINUX + +class Playing_sfx { +public: + Sfx* sfx; + Sample *sam; + Dword flags; + unsigned int vo, f, pos; + int pa; + unsigned int delta_inc, delta_position, inc; + Playing_sfx(Sfx* thesfx, Sample *thesam, Dword theflags=0); + virtual ~Playing_sfx(); +}; +#endif + +class Sfx { +#ifdef UGS_DIRECTX + IDirectSoundBuffer *buf; +#endif +#ifdef UGS_LINUX + friend class Playing_sfx; + Playing_sfx* playing; +#endif +public: + Sfx(Sample *sam, Dword dwPlayFlags=0, int vo=-1, int pa=-1, int f=-1, int pos=-1); + void stop(); + void pan(int pa); //-4000=gauche 0=centre 4000=droite + void freq(int pa); //200=bas 60000=tres haute + void volume(int pa); //0=full .. -4000=absent + void position(int pa); +#ifdef UGS_LINUX + virtual ~Sfx(); +#endif +}; + +extern Sound* sound; + +#endif diff --git a/skelton/include/spawn.h b/skelton/include/spawn.h new file mode 100644 index 0000000..56ddc1c --- /dev/null +++ b/skelton/include/spawn.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_SPAWN +#define _HEADER_SPAWN + +class Process { +public: + static Process *alloc(); + virtual ~Process(); + virtual void init(const char *fn, int argc, char *argv[])=0; + virtual bool done()=0; + virtual int get_exit_code()=0; +}; + +#endif diff --git a/skelton/include/sprite.h b/skelton/include/sprite.h new file mode 100644 index 0000000..0d39063 --- /dev/null +++ b/skelton/include/sprite.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_SPRITE +#define _HEADER_SPRITE +#include "types.h" +#include "utils.h" +#include "error.h" +#include "raw.h" +#include "bitmap.h" +#include "palette.h" + +#define CENTER (-123456) +#define CORNER (-123457) + +class Sprite: public Bitmap { + typedef Byte T; +public: + int hot_x; + int hot_y; + void set_hotspot(const int hx, const int hy); + Sprite(const Bitmap& b, const int hx=CENTER, const int hy=CENTER, const bool dx=false); + void draw(const Bitmap& d, const int dx, const int dy) const; + void draw(const Video_bitmap* d, const int dx, const int dy) const; + //void color_draw(const Remap& remap, const Bitmap& d, int dx, int dy) const; +}; + +class Font; + +class Fontdata { + friend class Font; + Sprite* spr[256]; // attention: il y a du lousse car ils ne servent pas tous + int shrink; // indique de combien 'overlapper' cette font + int pre_width[256]; // 'width' pre-calculer des symboles +public: + Fontdata(Res &res, int s=0); + Fontdata(const Fontdata &o); + virtual ~Fontdata(); + int width(const char *m) const; + int width(const char *m, int num) const; + int height() const { + return spr[1]->height; + } + int translate(const char **m) const; +}; + +class Font { +public: + const Fontdata& fdata_original; +private: + Fontdata fdata; +public: + Font(const Fontdata& f); + Font(const Fontdata& f, const Palette& dst, int r, int g, int b, int r2=0, int g2=0, int b2=0); + void colorize(const Palette& dst, int r, int g, int b, int r2=0, int g2=0, int b2=0); + void remap(const Remap *map); + void draw(const char *m, const Bitmap& b, int x, int y) const; + void draw(const char *m, const Video_bitmap* b, int x, int y) const; + int width(const char *m) const { + return fdata_original.width(m); + } + int width(const char *m, int num) const { + return fdata_original.width(m, num); + } + int height() const { + return fdata_original.height(); + } +}; + +#endif diff --git a/skelton/include/stringtable.h b/skelton/include/stringtable.h new file mode 100644 index 0000000..8601fc4 --- /dev/null +++ b/skelton/include/stringtable.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_STRINGTABLE +#define _HEADER_STRINGTABLE +#include "res.h" + +class Stringtable { + char **table; + int num; + char *buf; + bool mustfree; + void parse(char *buf, Dword size); +public: + Stringtable(const char *nam); + Stringtable(Byte *buf, Dword size); + virtual ~Stringtable(); + Stringtable& operator=(const Stringtable&); + int size() const { + return num; + } + const char *get(const int quel) const { + if(quel (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +typedef unsigned int Dword; +typedef unsigned short Word; +typedef unsigned char Byte; + +#endif diff --git a/skelton/include/unicode.h b/skelton/include/unicode.h new file mode 100644 index 0000000..503d9e1 --- /dev/null +++ b/skelton/include/unicode.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_UNICODE +#define _HEADER_UNICODE + +#include "types.h" +#include "buf.h" + +class Unicode { + Buf the_string; +public: + Unicode(char *s); + Unicode &cat(char *s); + operator Byte *(); + Dword size(); +}; + +#endif diff --git a/skelton/include/url.h b/skelton/include/url.h new file mode 100644 index 0000000..241d55f --- /dev/null +++ b/skelton/include/url.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_URL +#define _HEADER_URL + +#include "types.h" + +//NB: This is not a complete parser. URL fragments, parameters and +// query information will remain in the path unparsed +class Url { + //sizeof(path) should remain >= than all others + char scheme[16]; + char host[256]; + Word port; + char path[256]; +public: + Url(const char* u=""); + const char* getScheme() const; + const char* getHost() const; + Word getPort() const; + const char* getPath() const; + void getFull(char* buf) const; + void setScheme(const char* s); + void setHost(const char* h); + void setPort(const Word p); + void setPath(const char* p); + void setFull(const char* u); +}; + +#endif diff --git a/skelton/include/utils.h b/skelton/include/utils.h new file mode 100644 index 0000000..2791442 --- /dev/null +++ b/skelton/include/utils.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_UTILS +#define _HEADER_UTILS +#include "types.h" +#include "error.h" + +inline void mset(void* p, Byte c, Dword l) { + for(Dword i(0); i +inline T abs(const T n) { + if(n +inline void xchg(T& a, T& b) { + T tmp=a; + a=b; + b=tmp; +} + +class Vect { + public: + float x, y; + Vect(float x_, float y_): x(x_), y(y_) {} + virtual ~Vect() {} + float length2() const { + return x*x+y*y; + } + Vect operator+=(const Vect v) { + x+=v.x; + y+=v.y; + return *this; + } + Vect operator-=(const Vect v) { + x-=v.x; + y-=v.y; + return *this; + } + Vect operator+(const Vect v) const { + return Vect(x+v.x, y+v.y); + } + Vect operator-(const Vect v) const { + return Vect(x-v.x, y-v.y); + } + Vect operator-() const { + return Vect(-x, -y); + } + Vect operator*(const float f) const { + return Vect(x*f, y*f); + } + Vect operator/(const float f) const { + return Vect(x/f, y/f); + } + // dot-product + float operator*(const Vect v) const { + return x*v.x+y*v.y; + } + // cross-product + float crossprod(const Vect v) const { + return x*v.y-y*v.x; + } + Vect project(const Vect v) const { + float tmp=v*v; + if(tmp) + return v*((*this)*v/tmp); + else + return Vect(0.0f, 0.0f); + } + Vect transpose() const { + return Vect(y, x); + } +}; + +#endif diff --git a/skelton/include/video.h b/skelton/include/video.h new file mode 100644 index 0000000..9915431 --- /dev/null +++ b/skelton/include/video.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_VIDEO +#define _HEADER_VIDEO + +#include "clipable.h" +#include "palette.h" + +class Bitmap; +class Sprite; + +class Video_bitmap: public Clipable { +protected: + Video_bitmap(): Clipable(0, 0) { }; +public: + int pos_x, pos_y; + static Video_bitmap* New(const int px, const int py, + const int w, const int h, const int rw); + static Video_bitmap* New(const int px, const int py, + const int w, const int h); + virtual ~Video_bitmap() { }; + + /* remplis un rectangle a la position x,y de largeur w et hauteur h + avec la couleur color. */ + virtual void rect(const int x,const int y,const int w,const int h, + const int color) const = 0; + + /* rectangle vide (contour) a la position x,y de largeur w et hauteur h + avec la couleur color. */ + virtual void box(const int x,const int y,const int w,const int h, + const int color) const = 0; + + /* get un bout de bitmap à partir du Video_bitmap */ + virtual void get_bitmap(const Bitmap* bit, const int x, const int y, + const int w, const int h) const = 0; + + /* mets le pel à la position x,y a la couleur c */ + virtual void put_pel(const int x, const int y, const Byte c) const = 0; + + /* ligne horizontale partant de x,y et mettant w pels de couleur c + vers la droite (?) */ + virtual void hline(const int y, const int x, + const int w, const Byte c) const = 0; + + /* ligne horizontale partant de x,y et mettant h pels de couleur c + vers le bas (?) */ + virtual void vline(const int x, const int y, + const int h, const Byte c) const = 0; + + /* ligne quelquonque de à de couleur c */ + virtual void line(const int x1, const int y1, const int x2, const int y2, + const Byte c) const = 0; + + /* blit un Bitmap d a la position dx,dy */ + virtual void put_bitmap(const Bitmap& d, + const int dx, const int dy) const = 0; + + /* blit un Sprite (mask) d a la position dx,dy */ + virtual void put_sprite(const Sprite& d, + const int dx, const int dy) const = 0; + + /* ajuste la mem vidéo pointé par cette video_bitmap + dans la page vidéo en background */ + virtual void setmem() = 0; +}; + +class Video { +public: + bool xwindow; + Video_bitmap *vb; + Byte newpal; + Palette pal; + int width, height, bit; + int need_paint; + int pitch; + Dword framecount; + static Video* New(int w, int h, int b, const char *wname, bool dumb=false); + virtual ~Video() { }; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual void flip() = 0; + virtual void start_frame() = 0; + virtual void end_frame() = 0; + virtual void setpal(const Palette& p) = 0; + virtual void dosetpal(PALETTEENTRY pal[256], int size) = 0; + virtual void restore() = 0; + virtual void clean_up() = 0; + virtual void snap_shot(int x, int y, int w, int h) = 0; +}; + +extern Video* video; +extern bool video_is_dumb; + +#endif diff --git a/skelton/include/video_dumb.h b/skelton/include/video_dumb.h new file mode 100644 index 0000000..d51cc60 --- /dev/null +++ b/skelton/include/video_dumb.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_VIDEO_DUMB +#define _HEADER_VIDEO_DUMB + +#include "video.h" + +class Dumb_Video_bitmap: public Video_bitmap { +public: + int opos_y; + static Dumb_Video_bitmap* New(const int px, const int py, + const int w, const int h, + const int rw); + static Dumb_Video_bitmap* New(const int px, const int py, + const int w, const int h); + Dumb_Video_bitmap(const int px, const int py, const int w, const int h, + const int rw); + Dumb_Video_bitmap(const int px, const int py, const int w, const int h); + virtual void rect(const int x, const int y, + const int w, const int h, const int color) const; + virtual void box(const int x, const int y, const int w, const int h, + const int color) const; + virtual void get_bitmap(const Bitmap *bit, const int x, const int y, + const int w, const int h) const; + virtual void put_pel(const int x, const int y, const Byte c) const; + virtual void hline(const int y, const int x, + const int w, const Byte c) const; + virtual void vline(const int x, const int y, + const int w, const Byte c) const; + virtual void line(const int x1, const int y1, + const int x2, const int y2, const Byte c) const; + virtual void put_bitmap(const Bitmap& d, const int dx, const int dy) const; + virtual void put_sprite(const Sprite& d, const int dx, const int dy) const; + virtual void setmem(); +}; + +class Video_Dumb: public Video { +public: + int displayoffset; + static Video_Dumb* New(int w, int h, int b, const char *wname); + Video_Dumb(int w, int h, int b, const char *wname); + virtual ~Video_Dumb(); + virtual void lock(); + virtual void unlock(); + virtual void flip(); + virtual void start_frame(); + virtual void end_frame(); + virtual void setpal(const Palette& p); + virtual void dosetpal(PALETTEENTRY pal[256], int size); + virtual void restore(); + virtual void clean_up(); + virtual void snap_shot(int x, int y, int w, int h); +}; + +#endif /* _HEADER_VIDEO_DUMB */ diff --git a/skelton/include/video_dx.h b/skelton/include/video_dx.h new file mode 100644 index 0000000..c4adf5f --- /dev/null +++ b/skelton/include/video_dx.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_VIDEO_DX +#define _HEADER_VIDEO_DX + +#include "video.h" + +class DirectX_Surface { +public: + LPDIRECTDRAWSURFACE s; + Bitmap *b; + DirectX_Surface(LPDIRECTDRAWSURFACE s2, Bitmap *b2): + s(s2), b(b2) { + } +}; + +class DirectX_Video_bitmap: Video_bitmap { +public: + Bitmap* currentpage; + DirectX_Video_bitmap(const int px, const int py, const int w, const int h, + const int rw); + DirectX_Video_bitmap(const int px, const int py, const int w, const int h); + virtual ~DirectX_Video_bitmap(); + virtual void rect(const int x, const int y, + const int w, const int h, const int color) const; + virtual void box(const int x, const int y,const int w, const int h, const int color) const; + virtual void get_bitmap(const Bitmap *bit, const int x, const int y, + const int w, const int h) const; + virtual void put_pel(const int x, const int y, const Byte c) const; + virtual void hline(const int y, const int x, + const int w, const Byte c) const; + virtual void vline(const int x, const int y, + const int w, const Byte c) const; + virtual void line(const int x1, const int y1, + const int x2, const int y2, const Byte c) const; + virtual void put_bitmap(const Bitmap& d, const int dx, const int dy) const; + virtual void put_sprite(const Sprite& d, const int dx, const int dy) const; + virtual void setmem(); +}; + +class DirectX_Video: public Video { + void create_palette(); +public: + LPDIRECTDRAW lpdd; + LPDIRECTDRAWSURFACE lpddsprimary, lpddsback; + LPDIRECTDRAWPALETTE lpddpal; + DDSURFACEDESC ddsdlock; + static int screen_shot; + Array surfaces; + void add_surface(LPDIRECTDRAWSURFACE s, Bitmap *b); + void remove_surface(LPDIRECTDRAWSURFACE s, Bitmap *b); + DirectX_Video(int w, int h, int b, const char *wname); + virtual ~DirectX_Video(); + void lock(); + void unlock(); + void flip(); + void start_frame(); + void end_frame(); + void setpal(const Palette& p); + virtual void dosetpal(PALETTEENTRY pal[256], int size); + void restore(); + void clean_up(); + void snap_shot(int x, int y, int w, int h); +}; + +#endif /* _HEADER_VIDEO_DX */ diff --git a/skelton/include/video_dx16.h b/skelton/include/video_dx16.h new file mode 100644 index 0000000..8a3a224 --- /dev/null +++ b/skelton/include/video_dx16.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_VIDEO_DX16 +#define _HEADER_VIDEO_DX16 + +#include "video.h" + +class Surface_basedx; +class Surface; + +class Video16: public Video { +public: + LPDIRECTDRAW lpdd; + LPDIRECTDRAWSURFACE lpddsprimary, lpddsback; + LPDIRECTDRAWPALETTE lpddpal; + DDSURFACEDESC ddsdlock; + Surface_basedx *back; + Array surfaces; + static int screen_shot; + int green_high; + Video16(int w, int h, int b, const char *wname); + virtual ~Video16(); + void lock(); + void unlock(); + void flip(); + void start_frame(); + void end_frame(); + void setpal(const Palette& p); + virtual void dosetpal(PALETTEENTRY pal[256], int size); + void restore(); + void clean_up(); + void snap_shot(int x, int y, int w, int h); + void add_surface(Surface *s); + void remove_surface(Surface *s); +}; + +#endif /* _HEADER_VIDEO_DX16 */ diff --git a/skelton/include/video_svga.h b/skelton/include/video_svga.h new file mode 100644 index 0000000..908de82 --- /dev/null +++ b/skelton/include/video_svga.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_VIDEO_SVGA +#define _HEADER_VIDEO_SVGA + +#include "video.h" + +class Svgalib_Video_bitmap: public Video_bitmap { +public: + int opos_y; + static Svgalib_Video_bitmap* New(const int px, const int py, + const int w, const int h, + const int rw); + static Svgalib_Video_bitmap* New(const int px, const int py, + const int w, const int h); + Svgalib_Video_bitmap(const int px, const int py, const int w, const int h, + const int rw); + Svgalib_Video_bitmap(const int px, const int py, const int w, const int h); + virtual void rect(const int x, const int y, + const int w, const int h, const int color) const; + virtual void box(const int x, const int y, const int w, const int h, + const int color) const; + virtual void get_bitmap(const Bitmap *bit, const int x, const int y, + const int w, const int h) const; + virtual void put_pel(const int x, const int y, const Byte c) const; + virtual void hline(const int y, const int x, + const int w, const Byte c) const; + virtual void vline(const int x, const int y, + const int w, const Byte c) const; + virtual void line(const int x1, const int y1, + const int x2, const int y2, const Byte c) const; + virtual void put_bitmap(const Bitmap& d, const int dx, const int dy) const; + virtual void put_sprite(const Sprite& d, const int dx, const int dy) const; + virtual void setmem(); +}; + +class Svgalib_Video: public Video { +public: + int displayoffset; + static Svgalib_Video* New(int w, int h, int b, const char *wname); + Svgalib_Video(int w, int h, int b, const char *wname); + virtual ~Svgalib_Video(); + virtual void lock(); + virtual void unlock(); + virtual void flip(); + virtual void start_frame(); + virtual void end_frame(); + virtual void setpal(const Palette& p); + virtual void dosetpal(PALETTEENTRY pal[256], int size); + virtual void restore(); + virtual void clean_up(); + virtual void snap_shot(int x, int y, int w, int h); +}; + +#endif /* _HEADER_VIDEO_SVGA */ diff --git a/skelton/include/video_x11.h b/skelton/include/video_x11.h new file mode 100644 index 0000000..605291b --- /dev/null +++ b/skelton/include/video_x11.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_VIDEO_X11 +#define _HEADER_VIDEO_X11 + +#define Font XFont +#define Cursor XCursor +#include +#include +#undef Font +#undef Cursor + +#include "video.h" + +class Video_bitmap_X11: public Video_bitmap { +private: + Bitmap* fb; +public: + static Video_bitmap* New(const int px, const int py, + const int w, const int h, const int rw); + static Video_bitmap* New(const int px, const int py, + const int w, const int h); + Video_bitmap_X11(const int px, const int py, + const int w, const int h, const int rw); + Video_bitmap_X11(const int px, const int py, + const int w, const int h); + virtual ~Video_bitmap_X11(); + virtual void rect(const int x,const int y,const int w,const int h, + const int color) const; + virtual void box(const int x,const int y,const int w,const int h, + const int color) const; + virtual void get_bitmap(const Bitmap* bit, const int x, const int y, + const int w, const int h) const; + virtual void put_pel(const int x, const int y, const Byte c) const; + virtual void hline(const int y, const int x, + const int w, const Byte c) const; + virtual void vline(const int x, const int y, + const int h, const Byte c) const; + virtual void line(const int x1, const int y1, const int x2, const int y2, + const Byte c) const; + virtual void put_bitmap(const Bitmap& d, + const int dx, const int dy) const; + virtual void put_sprite(const Sprite& d, + const int dx, const int dy) const; + virtual void setmem(); +}; + +class Video_X11: public Video { +public: + Display *display; + XImage *image; + unsigned char* vfb; + short min_x[480], max_x[480]; + int min_x2, max_x2, min_y2, max_y2; + Window window; + GC gc; + Visual *visual; + Atom delete_win; + int depth; + XShmSegmentInfo shminfo; + bool do_shm; + static Video* New(int w, int h, int b, const char *wname); + Video_X11(int w, int h, int b, const char *wname, Display*, Visual*, int d); + virtual ~Video_X11(); + virtual void dirty(int x1, int y1, int x2, int y2); + virtual void dirty2(int x1, int y1, int x2, int y2); + virtual void lock(); + virtual void unlock(); + virtual void start_frame(); + virtual void flip(); + virtual void end_frame(); + virtual void setpal(const Palette& p) ; + virtual void restore(); + virtual void clean_up(); + virtual void snap_shot(int x, int y, int w, int h); +}; + +class Video_X11_8: public Video_X11 { +public: + Colormap cmap; + XColor colors[256]; + Video_X11_8(int w, int h, int b, const char *wname, Display*, Visual*); + virtual ~Video_X11_8(); + virtual void flip(); + virtual void dosetpal(PALETTEENTRY pal[256], int size); +}; + +class Video_X11_16: public Video_X11 { +public: + unsigned short colors[256]; + Video_X11_16(int w, int h, int b, const char *wname, Display*, Visual*, int); + virtual ~Video_X11_16(); + virtual void flip(); + virtual void dosetpal(PALETTEENTRY pal[256], int size); +}; + +class Video_X11_24: public Video_X11 { +public: + struct Color { + unsigned char red; + unsigned char green; + unsigned char blue; + }; + unsigned long colors[256]; + Video_X11_24(int w, int h, int b, const char *wname, Display*, Visual*); + virtual ~Video_X11_24(); + virtual void flip(); + virtual void dosetpal(PALETTEENTRY pal[256], int size); +}; + +#endif /* _HEADER_VIDEO_X11 */ diff --git a/skelton/include/zone_text_clock.h b/skelton/include/zone_text_clock.h new file mode 100644 index 0000000..dc56ff8 --- /dev/null +++ b/skelton/include/zone_text_clock.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifndef _HEADER_ZONE_TEXT_CLOCK +#define _HEADER_ZONE_TEXT_CLOCK + +#include "inter.h" + +class Zone_text_clock: public Zone_text_field { +protected: + char timebuf[256]; +public: + Zone_text_clock(Inter* in, Dword *s, int px, int py, int pw, bool frame, Font *f2=NULL); + virtual ~Zone_text_clock(); + virtual void draw(); +}; + +#endif diff --git a/skelton/lib/dir.mk b/skelton/lib/dir.mk new file mode 100644 index 0000000..945f6b4 --- /dev/null +++ b/skelton/lib/dir.mk @@ -0,0 +1,31 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +ifndef MAKEFILE_INCLUDED + +.PHONY: local + +local: + $(MAKE) -C .. + +else +ifndef RULES + +TARGETS+=lib/libugs.a lib/libugs.so +CLEANS+=lib/libugs.a lib/libugs.so + +ifdef SOCKS +SOCKS_EXTRA:=-lsocks5 +endif + +else + +lib/libugs.a: $(OBJECTS) + $(AR) $(ARFLAGS) $@ $? + +lib/libugs.so: $(OBJECTS) $(SOCKS_EXTRA) + rm -f $@ + $(LINK.cc) $(LOADLIBES) $(LDLIBS) -shared $^ -o $@ + +endif +endif diff --git a/skelton/svgalib/cursor_x11.cpp b/skelton/svgalib/cursor_x11.cpp new file mode 100644 index 0000000..8e925aa --- /dev/null +++ b/skelton/svgalib/cursor_x11.cpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "video.h" +#include "cursor_x11.h" + +Cursor_X11::Cursor_X11() { + visible = true; +} + +void Cursor_X11::change_cursor(Sprite* s) { +} + +void Cursor_X11::restore_back(bool r) { +} + +void Cursor_X11::set_pos(int px, int py) { + x = px; + y = py; +} + +void Cursor_X11::set_limit(int x1, int y1, int x2, int y2) { +} + +void Cursor_X11::set_speed(const Byte s) { +} + +void Cursor_X11::move() { +} + +void Cursor_X11::get_back() { +} + +void Cursor_X11::put_back() { +} + +void Cursor_X11::draw() const { +} diff --git a/skelton/svgalib/dir.mk b/skelton/svgalib/dir.mk new file mode 100644 index 0000000..be3d191 --- /dev/null +++ b/skelton/svgalib/dir.mk @@ -0,0 +1,22 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +ifndef MAKEFILE_INCLUDED + +.PHONY: local + +local: + $(MAKE) -C .. + +else +ifndef RULES + +DEPENDS+=$(wildcard svgalib/*.cpp) + +ifeq "$(TARGET)" "linux" +OBJECTS+=$(patsubst %.cpp,%.o,$(wildcard svgalib/*.cpp)) +endif + +else +endif +endif diff --git a/skelton/svgalib/error.cpp b/skelton/svgalib/error.cpp new file mode 100644 index 0000000..54452b5 --- /dev/null +++ b/skelton/svgalib/error.cpp @@ -0,0 +1,70 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include "error.h" +#include "video.h" + +#ifdef _DEBUG + int copper=0; +#endif + +#ifdef _DEBUG + bool _debug = true; +#else + bool _debug = false; +#endif + +bool skelton_debug = true; + +Error::Error(const char* m, ...) { + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + fprintf(stderr, "Error: %s\n", st); + exit(1); +} + +void msgbox(const char* m, ...) { + if(_debug) { + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + fprintf(stderr, st); + } +} + +void skelton_msgbox(const char* m, ...) { + if(_debug && skelton_debug) { + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + fprintf(stderr, st); + } +} + +void lock_msgbox(const char* m, ...) { + if(_debug) { + char st[1024]; + va_list marker; + va_start(marker, m); + vsprintf(st, m, marker); + va_end(marker); + fprintf(stderr, st); + } +} + +void user_output(const char* title, const char *msg) { + printf("%s\n%s\n",title,msg); +} diff --git a/skelton/svgalib/find_file.cpp b/skelton/svgalib/find_file.cpp new file mode 100644 index 0000000..89d8f57 --- /dev/null +++ b/skelton/svgalib/find_file.cpp @@ -0,0 +1,95 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#define __USE_GNU +#include +#include +#include +#include + +#include "find_file.h" + +Find_file_entry::Find_file_entry(const char *n, bool f) { + name[0] = 0; + name[1023] = 0; + date[0] = 0; + date[1023] = 0; + strncpy(name, n, 1023); + size = 0; + is_folder = f; +} + +class Find_file_Unix: public Find_file { +private: + glob_t globbuf; + int count; + struct stat sbuf; +public: + Find_file_Unix(const char *n); + virtual ~Find_file_Unix(); + virtual bool eof(); + virtual Find_file_entry get_next_entry(); +}; + +Find_file *Find_file::New(const char *n) { + return new Find_file_Unix(n); +} + +void Find_file::get_current_directory(char *s) { + if(getcwd(s, 1024) == NULL) + s[0] = 0; + //Remove slashes from end + char *w=s+strlen(s); + while(w>=s) { + if(*w=='/') + *w=0; + else + break; + w--; + } +} + +Find_file_Unix::Find_file_Unix(const char *n) { + count = -1; + + globbuf.gl_offs = 0; + + glob(n, 0, NULL, &globbuf); +} + +Find_file_Unix::~Find_file_Unix() { + globfree(&globbuf); +} + +bool Find_file_Unix::eof() { + return !(globbuf.gl_pathc-count); +} + +Find_file_entry Find_file_Unix::get_next_entry() { + struct stat sbuf; + char* buf; + bool isdir; + + if(count == -1) + buf = ".."; + else + buf = globbuf.gl_pathv[count]; + + count++; + + if(stat(buf, &sbuf) == -1) + isdir = false; + else + isdir = S_ISDIR(sbuf.st_mode); + + Find_file_entry f(basename(buf), isdir); + + f.size = sbuf.st_size; + strncpy(f.date, ctime(&sbuf.st_mtime), 1023); + + return f; +} diff --git a/skelton/svgalib/input.cpp b/skelton/svgalib/input.cpp new file mode 100644 index 0000000..d79aa74 --- /dev/null +++ b/skelton/svgalib/input.cpp @@ -0,0 +1,30 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "video.h" +#include "input_dumb.h" +#ifdef UGS_LINUX_X11 +#include "input_x11.h" +#endif +#ifdef UGS_LINUX_SVGA +#include "input_svga.h" +#endif + +Input *input = NULL; + +Input* Input::New(bool dumb) { + if(dumb) + return new Input_Dumb(); +#ifdef UGS_LINUX_X11 + if(video->xwindow) + return new Input_X11; + else +#endif +#ifdef UGS_LINUX_SVGA + return new Input_Svgalib; +#endif + return NULL; +} diff --git a/skelton/svgalib/input_svga.cpp b/skelton/svgalib/input_svga.cpp new file mode 100644 index 0000000..1bdb3b5 --- /dev/null +++ b/skelton/svgalib/input_svga.cpp @@ -0,0 +1,231 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#if 1 +#define Palette gl_palette +#include +#include +#include +#undef Palette +#else +#include "svgalib.h" +#endif +#include "video.h" +#include "input_svga.h" + +const char *keynames[256] = { + "", "Escape", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "0", "-", "=", "Backspace", "Tab", + "Q", "W", "E", "R", "T", "Y", "U", "I", + "O", "P", "[", "]", "Enter", "Ctrl", "A", "S", + "D", "F", "G", "H", "J", "K", "L", ";", + "'", "`", "Left shift", "\\", "Z", "X", "C", "V", + "B", "N", "M", ",", ".", "/", "Right shift", "Pad *", + "Alt", "Space", "Caps lock", "F1", "F2", "F3", "F4", "F5", + "F6", "F7", "F8", "F9", "F10", "Num lock", "Scrl lock", "Pad 7", + "Pad 8", "Pad 9", "Pad -", "Pad 4", "Pad 5", "Pad 6", "Pad +", + "Pad 1", "Pad 2", "Pad 3", "Pad 0", "Pad .", "Print scrn", "", + "<", "F11", "F12", "", "", "", "", "", "", "","Pad Enter", + "Right Ctrl", "Pad /", "PrintScrn", "Alt Char", "Pause", + "Home", "Up", "Page Up", "Left", "Right", "End", "Down", + "Page Down", "Insert", "Delete", + "", "", "", "", "", "", "", "Pause", + "", "", "", "", "", "Win left", "Win right", "Win popup", + "", "Pause", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "Pad Enter", "2nd Ctrl", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "Pad /", "", "", + "2nd Alt", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "Home", + "Up", "Page up", "", "Left", "", "Right", "", "End", + "Down", "Page down", "Insert", "Del", "", "", "", "", + "", "", "", "Win left", "Win right", "Win popup", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "Macro", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "" +}; + +Input_Svgalib::Input_Svgalib() { + struct sigaction newsignals; + + tty_fd = 0; + + pause = false; + + clear_key(); + + mouse.dx = mouse.dy = mouse.dz = 0; + restore_mouse(); + + tcgetattr(0, &termattr); + + israw = false; + reraw(); + + newsignals.sa_handler = Input_Svgalib::signal_handler; + sigemptyset(&newsignals.sa_mask); + newsignals.sa_flags = 0; + newsignals.sa_restorer = NULL; + sigaction(SIGUSR2, &newsignals, &oldsignals); +} + +Input_Svgalib::~Input_Svgalib() { + sigaction(SIGUSR2, &oldsignals, NULL); + mouse_close(); + deraw(); + + tcsetattr(0, TCSANOW, &termattr); +} + +void Input_Svgalib::clear_key() { + shift_key = 0; + quel_key = -1; + key_pending = 0; + for(int i=0; i<256; i++) + keys[i] = 0; + if(israw) + keyboard_clearstate(); +} + +void Input_Svgalib::check() { + if(video) { + if(mouse_reinit) { + video->restore(); + restore_mouse(); + } + } + + process_key(); + process_mouse(); +} + +void Input_Svgalib::deraw() { + if(israw) { + keyboard_close(); + israw=false; + } +} + +void Input_Svgalib::reraw() { + if(!israw) { + keyboard_init(); + keyboard_seteventhandler(Input_Svgalib::keyboard_handler); + keyboard_translatekeys(8 /* DONT_CATCH_CTRLC */); + israw=true; + } +} + +void Input_Svgalib::process_key() { + int thekey; + + if(israw) + keyboard_update(); + else { + fflush(stdin); + thekey = vga_getkey(); + if(thekey) { + switch (thekey) { + case 27: + quel_key = KEY_ESCAPE; + break; + case 13: + case 10: + quel_key = KEY_ENTER; + break; + default: + if(key_pending < MAXKEY) { + key_buf[key_pending].c = thekey; + key_buf[key_pending].special = false; + key_pending++; + } + } + } + } +} + +void Input_Svgalib::process_mouse() { + mouse.dx = mouse.dy = mouse.dz = 0; + mouse_update(); +} + +void Input_Svgalib::restore_mouse() { + mouse_reinit = false; + mouse_seteventhandler(Input_Svgalib::mouse_handler); + mouse.quel = -1; + for(int i=0; i<4; i++) + mouse.button[i] = RELEASED; +} + +void Input_Svgalib::mouse_handler(int button, + int dx, int dy, int dz, + int drx, int dry, int drz) { + input->mouse.dx += dx; + input->mouse.dy += dy; + input->mouse.dz += dz; + + if(input->mouse.button[0] == RELEASED && button & 4) + input->mouse.quel = 0; + if(input->mouse.button[1] == RELEASED && button & 2) + input->mouse.quel = 1; + if(input->mouse.button[2] == RELEASED && button & 1) + input->mouse.quel = 2; + + input->mouse.button[0] = (Byte) ((button & 4) ? PRESSED:RELEASED); + input->mouse.button[1] = (Byte) ((button & 2) ? PRESSED:RELEASED); + input->mouse.button[2] = (Byte) ((button & 1) ? PRESSED:RELEASED); +} + +void Input_Svgalib::keyboard_handler(int scancode, int press) { + if(press) { + input->keys[scancode] |= PRESSED; + switch(scancode) { + case KEY_RSHIFT: + case KEY_LSHIFT: + input->shift_key |= SHIFT; + break; + case KEY_RALT: + case KEY_LALT: + input->shift_key |= ALT; + break; + case KEY_RCTRL: + case KEY_LCTRL: + input->shift_key |= CONTROL; + break; + case 101: + case 119: + input->pause = true; + break; + default: + input->quel_key = scancode; + } + } else { + input->keys[scancode] = RELEASED; + switch(scancode) { + case KEY_RSHIFT: + case KEY_LSHIFT: + input->shift_key &= ~SHIFT; + break; + case KEY_RALT: + case KEY_LALT: + input->shift_key &= ~ALT; + break; + case KEY_RCTRL: + case KEY_LCTRL: + input->shift_key &= ~CONTROL; + break; + } + } +} + +void Input_Svgalib::signal_handler(int signal) { + /* indique que la souris devra etre re-initer au prochain refresh */ + ((Input_Svgalib*)input)->mouse_reinit = true; + ((Input_Svgalib*)input)->oldsignals.sa_handler(signal); +} diff --git a/skelton/svgalib/input_x11.cpp b/skelton/svgalib/input_x11.cpp new file mode 100644 index 0000000..23a297a --- /dev/null +++ b/skelton/svgalib/input_x11.cpp @@ -0,0 +1,303 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "cursor.h" +#include "main.h" +#include "input_x11.h" +#include "video_x11.h" +#include + +static KeyCode xlate[256]; + +Input_X11::Input_X11() { + int i; + Video_X11* videox11; + + videox11 = dynamic_cast(video); + + mouse.dx = mouse.dy = mouse.dz = 0; + + mouse.quel = -1; + for(i = 0; i < 4; i++) + mouse.button[i] = RELEASED; + + pause = false; + + clear_key(); + + /* J'ai le gros nerf du cou toute sorti et j'ai les cheveux d'rette + sur la tête. */ + for(i = 0; i < 256; i++) + xlate[i] = 0; + + for(i = 9; i < 92; i++) + xlate[i] = i - 8; + + //xlate[] = SCANCODE_LESS; // 86 + //xlate[] = SCANCODE_BREAK; // 101 + + for(i = 94; i < 97; i++) + xlate[i] = i - 8; + + for(i = 97; i < 101; i++) + xlate[i] = i + 5; + + for(i = 102; i < 108; i++) + xlate[i] = i + 4; + + xlate[108] = 96; /* SCANCODE_KEYPADENTER */ + xlate[109] = 97; /* SCANCODE_RIGHTCONTROL */ + xlate[110] = 119; /* SCANCODE_BREAK_ALTERNATIVE */ + xlate[111] = 99; /* SCANCODE_PRINTSCREEN */ + xlate[112] = 98; /* SCANCODE_KEYPADDIVIDE */ + xlate[113] = 100; /* SCANCODE_RIGHTALT */ + + xlate[115] = 219; /* Win left */ + xlate[116] = 220; /* Win right */ + xlate[117] = 221; /* Win popup */ + + im = XOpenIM(videox11->display, NULL, NULL, NULL); + if(im) { + ic = XCreateIC(im, + XNInputStyle, XIMPreeditNothing|XIMStatusNothing, + XNClientWindow, videox11->window, + XNFocusWindow, videox11->window, + NULL); + if(!ic) { + msgbox("Warning: unsupported input method\n"); + XCloseIM(im); + im = NULL; + } + } else { + msgbox("Warning: error opening X input method\n"); + ic = NULL; + } + + reraw(); +} + +Input_X11::~Input_X11() { + if(ic) + XDestroyIC(ic); + + if(im) + XCloseIM(im); +} + +void Input_X11::clear_key() { + int i; + + shift_key = 0; + quel_key = -1; + key_pending = 0; + + for(i = 0; i < 256; i++) + keys[i] = 0; +} + +void Input_X11::check() { + Video_X11* videox11; + int pending; + + if(!video) + return; + + videox11 = dynamic_cast(video); + + for(pending = XPending(videox11->display); + pending > 0; + pending--) { + XEvent event; + + XNextEvent(videox11->display, &event); + + if(!XFilterEvent(&event, None)) + switch(event.type) { + case KeyPress: + case KeyRelease: + process_key(event); + break; + + case ButtonPress: + case ButtonRelease: + case LeaveNotify: + case MotionNotify: + process_mouse(event); + break; + + case FocusIn: + alt_tab = false; + break; + + case FocusOut: + alt_tab = true; + break; + + case ClientMessage: + if((Atom)event.xclient.data.l[0] == videox11->delete_win) + quit_game(); + break; + + case Expose: + if(videox11) + videox11->dirty2(event.xexpose.x, + event.xexpose.y, + event.xexpose.x+event.xexpose.width, + event.xexpose.y+event.xexpose.height); + break; + + default: + skelton_msgbox("Unknown XEvent (%i)\n", event.type); + break; + } + } +} + +void Input_X11::process_key(XEvent event) { + KeyCode key = xlate[event.xkey.keycode]; + char buf[20]; + KeySym keysym; + Status status; + int num; + + switch(event.type) { + case KeyPress: + keys[key] |= PRESSED; + + if(!key) + skelton_msgbox("Unknown KeyCode: %i\n", event.xkey.keycode); + + if(israw) { + switch(key) { + case KEY_RSHIFT: + case KEY_LSHIFT: + shift_key |= SHIFT; + break; + case KEY_RALT: + case KEY_LALT: + shift_key |= ALT; + break; + case KEY_RCTRL: + case KEY_LCTRL: + shift_key |= CONTROL; + break; + case 101: + case 119: + pause = true; + break; + default: + quel_key = key; + } + } else { + if(ic) { + num = XmbLookupString(ic, &event.xkey, buf, 20, &keysym, &status); + } else { + num = XLookupString(&event.xkey, buf, 20, &keysym, NULL); + } + if(num) { + switch(buf[0]) { + case 27: + quel_key = KEY_ESCAPE; + break; + case 10: + case 13: + quel_key = KEY_ENTER; + break; + default: + if(key_pending < MAXKEY) { + key_buf[key_pending].c = buf[0]; + key_buf[key_pending].special = false; + key_pending++; + } + } + } + } + break; + + case KeyRelease: + keys[key] = RELEASED; + switch(key) { + case KEY_RSHIFT: + case KEY_LSHIFT: + shift_key &= ~SHIFT; + break; + case KEY_RALT: + case KEY_LALT: + shift_key &= ~ALT; + break; + case KEY_RCTRL: + case KEY_LCTRL: + shift_key &= ~CONTROL; + break; + } + break; + + default: + skelton_msgbox("process_key got non-key event\n"); + break; + } +} + +void Input_X11::process_mouse(XEvent event) { + switch(event.type) { + case ButtonPress: + switch(event.xbutton.button) { + case 1: + if(mouse.button[0] == RELEASED) + mouse.quel = 0; + mouse.button[0] = PRESSED; + break; + case 2: + if(mouse.button[1] == RELEASED) + mouse.quel = 1; + mouse.button[1] = PRESSED; + break; + case 3: + if(mouse.button[2] == RELEASED) + mouse.quel = 2; + mouse.button[2] = PRESSED; + break; + } + break; + + case ButtonRelease: + switch(event.xbutton.button) { + case 1: + mouse.button[0] = RELEASED; + break; + case 2: + mouse.button[1] = RELEASED; + break; + case 3: + mouse.button[2] = RELEASED; + break; + } + break; + + case LeaveNotify: + cursor->set_pos(-1, -1); + break; + + case MotionNotify: + if(cursor) + cursor->set_pos(event.xmotion.x, event.xmotion.y); + break; + + default: + skelton_msgbox("process_mouse got non-mouse event\n"); + break; + } +} + +void Input_X11::deraw() { + israw = false; +} + +void Input_X11::reraw() { + israw = true; +} + diff --git a/skelton/svgalib/main.cpp b/skelton/svgalib/main.cpp new file mode 100644 index 0000000..c018d8f --- /dev/null +++ b/skelton/svgalib/main.cpp @@ -0,0 +1,162 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +/* version Linux */ +#ifdef SOCKS +#include +#include +extern "C" int SOCKSinit(char *); +#endif +#ifdef _DEBUG +#include +#endif +#include +#include + +#include "debug.h" +#include "types.h" +#include "net.h" +#include "video.h" +#include "palette.h" +#include "input.h" +#include "sound.h" +#include "cursor.h" +#include "music.h" +#include "stringtable.h" +#include "overmind.h" +#include "resfile.h" +#include "command.h" +#include "main.h" + +bool alt_tab = false; +Time_mode time_control = TIME_NORMAL; +char cmd_line[1024]; +void quit_game(); + +void start_frame() { + if(sound) + sound->process(); + input->check(); + video->start_frame(); +} + +void end_frame() { + video->end_frame(); +} + +char exe_directory[1024]; + +static bool ignore_sigpipe=false; + +int main(int ARGC, char **ARGV, char **ENV) { +#ifdef _DEBUG + mcheck(NULL); +#endif + INIT_DEBUG; +#ifdef SOCKS + SOCKSinit(ARGV[0]); +#endif + atexit(delete_obj); + struct sigaction signals; + if(sigaction(SIGPIPE, NULL, &signals) < 0) + skelton_msgbox("Can't get SIGPIPE signal handler, ignoring.\n"); + else + if(signals.sa_handler == SIG_DFL) { + signals.sa_handler = SIG_IGN; + if(sigaction(SIGPIPE, &signals, NULL) < 0) + skelton_msgbox("Can't set SIGPIPE signal handler, ignoring.\n"); + else + ignore_sigpipe=true; + } + else + skelton_msgbox("SIGPIPE handler isn't default, ignoring.\n"); + + //Copy the whole thing + strncpy(exe_directory, ARGV[0], sizeof(exe_directory)); + exe_directory[sizeof(exe_directory)-1]=0; + //Remove file name and final last / + char *temp=strrchr(exe_directory, '/'); + if(temp) + *temp=0; + + for(int i=1; istop(); + music->close(); + delete music; + music=NULL; + } + if(stringtable) { + msgbox("deleting stringtable...\n"); + delete stringtable; + stringtable=NULL; + } + if(cursor) { + msgbox("deleting cursor..\n"); + delete cursor; + cursor=NULL; + } + if(ignore_sigpipe) { + msgbox("restoring default SIGPIPE handler...\n"); + struct sigaction sigs; + if(sigaction(SIGPIPE, NULL, &sigs) < 0) + msgbox("Can't get signal, whatever...\n"); + else { + sigs.sa_handler = SIG_DFL; + if(sigaction(SIGPIPE, &sigs, NULL) < 0) + msgbox("Can't set signal, whatever...\n"); + ignore_sigpipe=false; + } + } + msgbox("ending delete_obj...\n"); +} + +void quit_game() { + if(video) + video->clean_up(); + exit(0); +} + +Dword getmsec() { + struct timeval thetime; + + gettimeofday(&thetime, NULL); + + return (thetime.tv_sec*1000)+(thetime.tv_usec/1000); +} diff --git a/skelton/svgalib/music.cpp b/skelton/svgalib/music.cpp new file mode 100644 index 0000000..c83b2af --- /dev/null +++ b/skelton/svgalib/music.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifdef _DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include "error.h" +#include "music.h" + +#define CDROM_DEVICE "/dev/cdrom" + +class MusicLinux: public Music { +private: + int fd; + int playing; + bool loop_all; + unsigned char starttrack; + unsigned char endtrack; +public: + MusicLinux(); + virtual ~MusicLinux(); + virtual void open(); + virtual void close(); + virtual void play(int quel, bool loop=false); + virtual void replay(); + virtual void stop(); +}; + +Music *music=NULL; + +Music* Music::alloc() { + return new MusicLinux; +} + +MusicLinux::MusicLinux() { + active = false; + open(); +} + +MusicLinux::~MusicLinux() { + stop(); +} + +void MusicLinux::play(int quel, bool loop) { + struct cdrom_ti ti; + int status; + + if(!active) + return; + + if(quel < starttrack) + quel = starttrack; + + if(loop_all = loop) + playing = starttrack; + else + playing = quel; + + ti.cdti_trk0 = quel; + ti.cdti_ind0 = 0; + ti.cdti_trk1 = endtrack; + ti.cdti_ind1 = 0; + + status = ioctl(fd, CDROMPLAYTRKIND, &ti); + +#ifdef _DEBUG + if(status < 0) + perror("CDROMPLAYTRKIND"); +#endif +} + +void MusicLinux::replay() { + play(playing, loop_all); +} + +void MusicLinux::stop() { + int status; + + if(!active) + return; + + status = ioctl(fd, CDROMSTOP); + +#ifdef _DEBUG + if(status != 0) + perror("CDROMPLAYTRKIND"); +#endif +} + +void MusicLinux::open() { + int status; + struct cdrom_tochdr tochdr; + + if(active) + return; + + if((fd = ::open(CDROM_DEVICE, O_RDONLY)) < 0) { +#ifdef _DEBUG + perror("open"); +#endif + return; + } + + status = ioctl(fd, CDROMREADTOCHDR, &tochdr); + if(status != 0) { +#ifdef _DEBUG + perror("CDROMREADTOCHDR"); +#endif + ::close(fd); + active = false; + return; + } + + starttrack = tochdr.cdth_trk0; + endtrack = tochdr.cdth_trk1; + + skelton_msgbox("start track = %i\nend track = %i\n", starttrack, endtrack); + + active = true; +} + +void MusicLinux::close() { + if(!active) + return; + + ::close(fd); + + active = false; +} + diff --git a/skelton/svgalib/palette.cpp b/skelton/svgalib/palette.cpp new file mode 100644 index 0000000..657d09f --- /dev/null +++ b/skelton/svgalib/palette.cpp @@ -0,0 +1,126 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include "types.h" +#include "palette.h" +#include "video.h" +#ifdef UGS_LINUX_X11 +#include "video_x11.h" +#endif + +Palette noir; + +void Palette::set() { + video->dosetpal(pal, size); +} + +void Palette::load(const Image& raw) { + size=raw.palettesize(); + int j=0; + for(int i(0); isize; i++) + findrgb(i, src->r(i), src->g(i), src->b(i)); + } +} + +void Remap::findrgb(const Byte m, const Byte r, const Byte g, const Byte b) { + int best_diff=9999999, best_i=0, diff; + for(int i=1; isetpal(dest); + currentframe=destframe; +} + +void Fade::newdest(const Palette& dst, int frame) { + dest=dst; + int j=0; + +#ifdef UGS_LINUX_X11 + /* shit, c'est effrayant */ + if(dynamic_cast(video)) + if(!dynamic_cast(video)) { + frame = frame / 4; + + /* évitont de planter avec une div par zéro ou quelques autre horreur */ + if(frame < 2) + frame = 2; + } +#endif + + for(int i(0); i<256; i++) { + delta[j]=((dest.pal[i].peRed<<7)-current[j++])/frame; + delta[j]=((dest.pal[i].peGreen<<7)-current[j++])/frame; + delta[j]=((dest.pal[i].peBlue<<7)-current[j++])/frame; + } + currentframe=0; + destframe=frame; +} + +int Fade::step() { + if(currentframe==destframe) + return 1; + else { + if(dynamic_cast(video)) + usleep(3000); + for(int i(0); i<768; i++) + current[i]+=delta[i]; + currentframe++; + return 0; + } +} + +void Fade::set() { + if(currentframe==destframe) + return; + if(currentframe==destframe-1) { + video->setpal(dest); + } else { + video->pal.set_size(256); + for(int i(0); i<256; i++) + video->pal.setcolor(i, current[i*3]>>7, current[i*3+1]>>7, current[i*3+2]>>7); + video->newpal = true; + } +} diff --git a/skelton/svgalib/registry.cpp b/skelton/svgalib/registry.cpp new file mode 100644 index 0000000..2f5ae16 --- /dev/null +++ b/skelton/svgalib/registry.cpp @@ -0,0 +1,85 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include +#if __GLIBC_MINOR__ == 1 +#include +#else +#include +#endif +#include "registry.h" + +class RegistryLinux: public Registry { +private: + DB* db; +public: + static Registry *alloc(); + RegistryLinux(); + virtual ~RegistryLinux(); + virtual void open(const char *n, const char *dir); + virtual void write(const char *k, char *v); + virtual void read(const char *k, char *buffer, unsigned long size); + virtual void close(); +}; + +Registry *Registry::alloc() { + return new RegistryLinux(); +} + +RegistryLinux::RegistryLinux() { + db = NULL; +} + +RegistryLinux::~RegistryLinux() { + this->close(); +} + +void RegistryLinux::open(const char *n, const char *dir) { + char fn[1024]; + sprintf(fn, "%s/registry", dir); + db = dbopen(fn, O_CREAT|O_RDWR, 0666, DB_HASH, NULL); +} + +void RegistryLinux::write(const char *k, char *v) { + DBT key, data; + + key.data = (void*)k; + key.size = strlen(k); + data.data = v; + data.size = strlen(v); + + if(db) + db->put(db, &key, &data, 0); +} + +void RegistryLinux::read(const char *k, char *buffer, unsigned long size) { + DBT key, data; + + key.data = (void*)k; + key.size = strlen(k); + data.data = NULL; + data.size = 0; + + memset(buffer, 0, size); + + if(db) + db->get(db, &key, &data, 0); + + if(size > data.size) + size = data.size; + + strncpy(buffer, (char*)data.data, size); +} + +void RegistryLinux::close() { + if(db) + db->close(db); + + db = NULL; +} diff --git a/skelton/svgalib/res.cpp b/skelton/svgalib/res.cpp new file mode 100644 index 0000000..ce38f5d --- /dev/null +++ b/skelton/svgalib/res.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "res.h" + +Res_mem::Res_mem() { + pos = 0; + _buf = NULL; +} + +Res_dos::Res_dos(const char *fil, Res_mode mode) { + int flag; + Error* error; + _buf = NULL; + exist = 1; + flag = 0; + switch(mode) { + case RES_READ: + case RES_TRY: + flag = O_RDONLY; + break; + case RES_WRITE: + flag = O_RDWR; + break; + case RES_CREATE: + flag = O_CREAT|O_TRUNC|O_RDWR; + break; + } + handle = open(fil, flag, 0666); + if(handle == -1) + if(mode == RES_TRY || mode == RES_CREATE) + exist = 0; + else + error = new Error("Unable to open file '%s'", fil); +} + +Dword Res_dos::size() { + struct stat buf; + fstat(handle, &buf); + return buf.st_size; +} + +Res_dos::~Res_dos() { + if(handle != -1) + close(handle); + if(_buf) + delete _buf; +} + +void Res_dos::position(Dword po) { + lseek(handle, po, SEEK_SET); +} + +int Res_dos::read(void *b, int nb) { + Error* error; + int n = ::read(handle, b, nb); + if(n < 0) + error = new Error("Error reading file"); + return n; +} + +void Res_dos::write(const void *b, int nb) { + Error* error; + if(::write(handle, b, nb) != nb) + error = new Error("Error writing file"); +} + +const void* Res_dos::buf() { + if(_buf) + return _buf; + _buf = new Byte[size()]; + if(_buf == NULL) + (void)new Error("Not enough memory"); + read(_buf, size()); + return _buf; +} + +bool Res_dos::eof() { + return (get_position() >= size()) ? true:false; +} + +Dword Res_dos::get_position() { + return lseek(handle, 0, SEEK_CUR); +} diff --git a/skelton/svgalib/sound.cpp b/skelton/svgalib/sound.cpp new file mode 100644 index 0000000..5ce2bc4 --- /dev/null +++ b/skelton/svgalib/sound.cpp @@ -0,0 +1,451 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include + +#include +#include +#include + +#include "error.h" +#include "types.h" + +#include "main.h" +#include "sound.h" + +#define SOUNDDEV "/dev/dsp" +#define CHUNKSIZEBITS 13 +#define CHANNELNUMBER 2 +#define SAMPLINGRATE 44100 +#define BITSPERSAMPLE 16 +#define MAXVOICES 8 +#define VOLUMESHIFT 2 + +/* Infos sur la technique: + * + * Ça consiste à loader les sons en les normalisant au bit/sample et + * frequence du device. En faisant ceci, ça nous permet de faire + * toutes les interpolations qu'on veut d'avance. + */ + +struct riff_header { + unsigned int signature; + unsigned int length; + unsigned int type; +}; + +struct chunk_header { + unsigned int type; + unsigned int size; +}; + +struct fmt_chunk { + unsigned short format; /* format type */ + unsigned short channels; /* number of channels + (i.e. mono, stereo...) */ + unsigned int sampling; /* sample rate */ + unsigned int bytespersec; /* for buffer estimation */ + unsigned short blockalign; /* block size of data */ + unsigned short bitspersample; /* Number of bits per sample of mono data */ +}; + +Sound *sound = NULL; + +Sound::Sound(): dspfd(-1), fragbuf(NULL), active(false) { + int i; + + dspfd = open(SOUNDDEV, O_WRONLY); + if(dspfd == -1) { + perror(SOUNDDEV); + return; + } + + i = 0x00020000 | CHUNKSIZEBITS; + if(ioctl(dspfd, SNDCTL_DSP_SETFRAGMENT, &i) == -1) { + perror(SOUNDDEV); + return; + } + + if(i != (0x00020000 | CHUNKSIZEBITS)) { + skelton_msgbox("sound: warning, could not set fragment size\n"); + } + + if(ioctl(dspfd, SNDCTL_DSP_GETBLKSIZE, &fragsize) == -1) { + perror(SOUNDDEV); + return; + } + + fragbuf = malloc(fragsize); + + bps = BITSPERSAMPLE; + if(ioctl(dspfd, SNDCTL_DSP_SAMPLESIZE, &bps) == -1) { + perror(SOUNDDEV); + return; + } + + if(bps != BITSPERSAMPLE) { + skelton_msgbox("sound: warning, could not set sample size to %i bits\n", BITSPERSAMPLE); + } + + channels = CHANNELNUMBER; + if(ioctl(dspfd, SNDCTL_DSP_CHANNELS, &channels) == -1) { + perror(SOUNDDEV); + return; + } + + if(channels != CHANNELNUMBER) { + skelton_msgbox("sound: warning, could not set number of channels\n"); + } + + if(channels != 1 && channels != 2) { + skelton_msgbox(" Sound card doesn't support neither mono or stereo output. Sound disabled.\n"); + return; + } + + sampling = SAMPLINGRATE; + if(ioctl(dspfd, SNDCTL_DSP_SPEED, &sampling) == -1) { + perror(SOUNDDEV); + return; + } + + if(sampling != SAMPLINGRATE) { + skelton_msgbox("sound: warning, could not set sampling rate\n"); + } + + active = true; + skelton_msgbox("Sound::Sound: opened succesfully.\n"); +} + +void Sound::process() { + unsigned int i; + audio_buf_info info; + + if(ioctl(dspfd, SNDCTL_DSP_GETOSPACE, &info) == -1) + return; + + if(info.fragments == 0) + return; + + memset(fragbuf, 0, fragsize); + unsigned int frag_temp = fragsize; + + if(bps == 16) + frag_temp = frag_temp >> 1; + + if(channels == 2) + frag_temp = frag_temp >> 1; + + for(i = 0; i < (unsigned int)plays.size(); i++) { + Playing_sfx* p = plays[i]; + + if(bps == 8) { // si output 8-bit + Byte* output = (Byte*)fragbuf; + Byte* input = (Byte*)p->sam->audio_data; + + for(unsigned int j=0; jpos); + Byte tmpr; + + tmpl = ((int) tmpl * p->vo) >> 8; + + if(channels == 2) { // stereo output + tmpr = tmpl; + if(p->pa > 0) + tmpl = ((int) tmpl * (p->pa)) >> 8; + else if(p->pa < 0) + tmpr = ((int) tmpr * (-p->pa)) >> 8; + + *output++ += tmpr; + } + + *output++ += tmpl; + + if(p->delta_position + p->delta_inc < p->delta_position) + p->pos++; /* si overflow du delta */ + + p->delta_position += p->delta_inc; + p->pos += p->inc; + if((unsigned int)p->pos >= p->sam->length) + break; + } + } else { // si output 16-bit + signed short* output = (signed short*)fragbuf; + signed short* input = (signed short*)p->sam->audio_data; + + for(unsigned int j=0; jpos); + tmpl = (tmpl * p->vo) >> 8; + if(channels == 2) { // stereo output + signed short tmpr = tmpl; + if(p->pa > 0) + tmpl = (tmpl * (p->pa)) >> 8; + else if(p->pa < 0) + tmpr = (tmpr * (-p->pa)) >> 8; + + *output++ += tmpr; + } + *output++ += tmpl; + if(p->delta_position + p->delta_inc < p->delta_position) { + p->pos++; /* si overflow du delta */ + } + + p->delta_position += p->delta_inc; + p->pos += p->inc; + if((unsigned int)p->pos >= p->sam->length) + break; + } + } + + if((unsigned int)p->pos >= p->sam->length) { + plays.remove(i); + i--; + delete p; + } + } + + write(dspfd, fragbuf, fragsize); +} + +void Sound::delete_sample(Sample *sam) { + // il faut detruire tout les sons qui utilisent un Sample avant de + // detruire ce dernier! + for(int i=0; isam == sam) { + plays.remove(i); + i--; + delete p; + } + } +} + +void Sound::start(Playing_sfx* play) { + plays.add(play); +} + +Sound::~Sound() { + if(dspfd != -1) + close(dspfd); + + if(fragbuf) + free(fragbuf); +} + +Sample::Sample(Res& re, int nb): audio_data(NULL), sampling(0), length(0) { + char *buffer; + Dword size; + + if(!sound) + return; + + buffer = (char *)re.buf(); + size = re.size(); + + loadriff(buffer, size); +} + +void Sample::loadriff(const char *res, unsigned int len) { + bool seenfmt = false; + + char *sample=NULL; + unsigned int size=0, bps=0; + + if(((struct riff_header *)res)->signature != 0x46464952) /* 'RIFF' */ + (void)new Error("Bad RIFF signature"); + + if(((struct riff_header *)res)->type != 0x45564157) /* 'WAVE' */ + (void)new Error("RIFF is not a WAVE"); + + char *ptr = (char *)res + sizeof(struct riff_header); + char *endptr = (char *)res + len - 3; + + while((ptr < endptr) && (ptr >= res)) { + char *data = ptr + sizeof(struct chunk_header); + struct chunk_header *header_ptr = (struct chunk_header *)ptr; + + switch(header_ptr->type) { + case 0x20746d66: /* 'fmt ' */ + seenfmt = true; + + if(((struct fmt_chunk *)data)->channels != 1) + (void)new Error("RIFF/WAVE: unsupported number of channels"); + + sampling = ((struct fmt_chunk *)data)->sampling; + bps = ((struct fmt_chunk *)data)->bitspersample; + size = 0; + break; + case 0x61746164: /* 'data' */ + if(!seenfmt) + (void)new Error("RIFF/WAVE: 'data' subchunk seen before 'fmt ' subchunk"); + + unsigned int chunksize; + chunksize = header_ptr->size; + sample = (char*)realloc(sample, size+chunksize); + memcpy(sample+size, data, chunksize); + size += chunksize; + + break; + default: + /* ignore unknown chunks/subchunks */ + break; + } + ptr += sizeof(struct chunk_header) + header_ptr->size; + } + + if(!sample) + (void)new Error("Error loading sample"); + + resample(sample, size, bps); + + free(sample); +} + +void Sample::resample(char* sample, unsigned int size, unsigned int bps) { + unsigned int i; + + length = (size * (sound->sampling >> 7)) / (sampling >> 7); + length = (length * sound->bps) / bps; + + audio_data = malloc(length); // length est en 'byte' ici + + if(!audio_data) + (void)new Error("Couldn't allocate sample"); + + if(bps == 8) { + if(sound->bps == 16) + length = length >> 1; // transforme length en 'short' + + unsigned int pos, inc, delta, delta_pos, old_pos; + pos = delta_pos = 0; + old_pos = 1; + inc = size / length; + delta = (unsigned int) (4294967295U / length) * (size % length); + + for(i = 0; i < length; i++) { + int tube; + if(pos == old_pos && ((bps == 8 && pos < size-1) || (bps == 16 && pos < size - 1))) { + if(sound->bps == 8) { + tube = (Byte)sample[pos+1] >> VOLUMESHIFT; + // interpolation cheap + tube = (tube+((Byte *)audio_data)[i-1]) >> 1; + } else { + tube = (128 - (Byte)sample[pos+1]) << (8-VOLUMESHIFT); + // interpolation cheap + tube = (tube+((signed short*)audio_data)[i-1]) >> 1; + } + } else { + if(sound->bps == 8) + tube = (Byte)sample[pos] >> VOLUMESHIFT; + else + tube = (128 - (Byte)sample[pos]) << (8-VOLUMESHIFT); + old_pos = pos; + } + if(sound->bps == 8) + ((Byte *)audio_data)[i] = tube; + else + ((signed short*)audio_data)[i] = tube; + + pos += inc; + if(delta_pos + delta < delta_pos) // si overflow du delta + pos++; + delta_pos += delta; + } + } else { + (void)new Error("Sound: wave 16-bit non supporte presentement"); + } +} + +Sample::~Sample() { + if(audio_data) + free(audio_data); +} + +void Sample::stop() { +} + +Playing_sfx::Playing_sfx(Sfx* thesfx, Sample *thesam, Dword theflags): + sfx(thesfx), sam(thesam), flags(theflags), vo(0), f(0), pos(0), + pa(0), delta_inc(0), delta_position(0), inc(0) { +} + +Playing_sfx::~Playing_sfx() { + if(sfx) + sfx->playing = NULL; +} + +Sfx::Sfx(Sample *sam, Dword dwPlayFlags, int vo, int pa, int f, int pos): + playing(NULL) { + if(!sound || !sam || !sound->active) + return; + + if(sound->plays.size() == MAXVOICES) + return; + + playing = new Playing_sfx(this, sam, dwPlayFlags); + + volume(vo); + pan(pa); + freq(f); + position(pos); + + sound->start(playing); +} + +void Sfx::stop() { + /* TODO: fu fu fu */ +} + +void Sfx::pan(int pa) { + if(!playing) // le son est deja fini!! + return; + if(pa < -4096) + pa = -4096; + if(pa > 4096) + pa = 4096; + pa = pa >> 4; + if(pa > 0) + playing->pa = 256-pa; + else if(pa < 0) + playing->pa = -pa-256; + else if(pa == 0) + playing->pa = 0; +} + +void Sfx::freq(int pa) { + if(!playing) // le son est deja fini!! + return; + pa = pa * sound->sampling / playing->sam->sampling; + // il faut ajuster la frequence demande selon la freq original du sample! + playing->f = pa; + playing->inc = pa / sound->sampling; // calcul l'increment 'entier' + // puis calcul l'increment 'delta' qui overflowera a 2^32 + playing->delta_inc = (unsigned int) (4294967295U / sound->sampling) * (pa % sound->sampling); +} + +void Sfx::volume(int pa) { + if(!playing) // le son est deja fini!! + return; + if(pa < -4096) + pa = -4096; + playing->vo = (pa+4096) >> 4; +} + +void Sfx::position(int pa) { + if(!playing) // le son est deja fini!! + return; + if(pa == -1) + pa = 0; + + pa = pa * sound->sampling / playing->sam->sampling; + // il faut ajuster la position demande selon la freq original du sample! + playing->pos = pa; + playing->delta_position = 0; +} + +Sfx::~Sfx() { + if(playing) + playing->sfx = NULL; +} diff --git a/skelton/svgalib/video.cpp b/skelton/svgalib/video.cpp new file mode 100644 index 0000000..39916b1 --- /dev/null +++ b/skelton/svgalib/video.cpp @@ -0,0 +1,61 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "video.h" +#include "video_dumb.h" +#ifdef UGS_LINUX_X11 +#include "video_x11.h" +#endif +#ifdef UGS_LINUX_SVGA +#include "video_svga.h" +#endif + +Video* video = NULL; + +Video_bitmap* Video_bitmap::New(const int px, const int py, + const int w, const int h, const int rw) { + Video_bitmap* obj; + +#ifdef UGS_LINUX_X11 + if((obj = Video_bitmap_X11::New(px, py, w, h, rw))) + return obj; + else +#endif +#ifdef UGS_LINUX_SVGA + return Svgalib_Video_bitmap::New(px, py, w, h, rw); +#endif + return NULL; +} + +Video_bitmap* Video_bitmap::New(const int px, const int py, + const int w, const int h) { + Video_bitmap* obj; + +#ifdef UGS_LINUX_X11 + if((obj = Video_bitmap_X11::New(px, py, w, h))) + return obj; + else +#endif +#ifdef UGS_LINUX_SVGA + return Svgalib_Video_bitmap::New(px, py, w, h); +#endif + return NULL; +} + +Video* Video::New(int w, int h, int b, const char *wname, bool dumb) { + if(dumb) + return Video_Dumb::New(w, h, b, wname); + Video* obj; +#ifdef UGS_LINUX_X11 + if((obj = Video_X11::New(w, h, b, wname))) { + return obj; + } else +#endif +#ifdef UGS_LINUX_SVGA + return Svgalib_Video::New(w, h, b, wname); +#endif + return NULL; +} diff --git a/skelton/svgalib/video_svga.cpp b/skelton/svgalib/video_svga.cpp new file mode 100644 index 0000000..610c249 --- /dev/null +++ b/skelton/svgalib/video_svga.cpp @@ -0,0 +1,282 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#if 1 +#define Palette gl_palette +#include +#include +#undef Palette +#else +#include "svgalib.h" +#endif + +#include "video_svga.h" +#include "utils.h" +#include "error.h" +#include "input.h" +#include "main.h" +#include "cursor.h" + +Svgalib_Video_bitmap* Svgalib_Video_bitmap::New(const int px, const int py, + const int w, const int h, + const int rw) { + if(getenv("DISPLAY")) + return NULL; + else + return new Svgalib_Video_bitmap(px, py, w, h, rw); +} + +Svgalib_Video_bitmap* Svgalib_Video_bitmap::New(const int px, const int py, + const int w, const int h) { + if(getenv("DISPLAY")) + return NULL; + else + return new Svgalib_Video_bitmap(px, py, w, h); +} + +Svgalib_Video_bitmap::Svgalib_Video_bitmap(const int px, const int py, + const int w, const int h, + const int rw) { + width = w; + height = h; + pos_x = px; + pos_y = py; +} + +Svgalib_Video_bitmap::Svgalib_Video_bitmap(const int px, const int py, + const int w, const int h) { + width = w; + height = h; + pos_x = px; + pos_y = py; +} + +void Svgalib_Video_bitmap::rect(const int x, const int y, + const int w, const int h, + const int color) const { + if(clip(x, y, w, h)) + return; + + gl_fillbox(clip_x1+pos_x, clip_y1+pos_y+((Svgalib_Video*)video)->displayoffset, + clip_w, clip_y2-clip_y1+1, color); +} + +void Svgalib_Video_bitmap::box(const int x, const int y, + const int w, const int h, + const int color) const { + hline(y, x, w, color); + hline(y+h-1, x, w, color); + vline(x, y, h, color); + vline(x+w-1, y, h, color); +} + +void Svgalib_Video_bitmap::get_bitmap(const Bitmap* bit, const int x, const int y, + const int w, const int h) const { + if(clip(x, y, w, h)) + return; + + if(clip_x1==x && clip_y1==y && + clip_w == bit->width && clip_y2 == y+bit->height-1) { + gl_getbox(clip_x1+pos_x, clip_y1+pos_y+((Svgalib_Video*)video)->displayoffset, clip_w, + clip_y2-clip_y1+1, (*bit)[0]); + } else { + int i; + for(i=clip_y1; i<=clip_y2; i++) + gl_getbox(clip_x1+pos_x, i+((Svgalib_Video*)video)->displayoffset+pos_y, clip_w, 1, + (*bit)[i-y]+clip_x1-x); + } +} + +void Svgalib_Video_bitmap::put_pel(const int x, const int y, const Byte c) const { + if(clip(x, y, 1, 1)) + return; + + gl_setpixel(clip_x1+pos_x, clip_y1+pos_y+((Svgalib_Video*)video)->displayoffset, c); +} + +void Svgalib_Video_bitmap::hline(const int y, const int x, + const int w, const Byte c) const { + if(clip(x, y, w, 1)) + return; + + gl_hline(clip_x1+pos_x, clip_y1+pos_y+((Svgalib_Video*)video)->displayoffset, + clip_x2+pos_x, c); +} + +void Svgalib_Video_bitmap::vline(const int x, const int y, const int h, + const Byte c) const { + if(clip(x, y, 1, h)) + return; + + gl_line(clip_x1+pos_x, clip_y1+pos_y+((Svgalib_Video*)video)->displayoffset, + clip_x1+pos_x, clip_y2+pos_y+((Svgalib_Video*)video)->displayoffset, c); +} + +void Svgalib_Video_bitmap::line(const int x1, const int y1, +const int x2, const int y2, const Byte c) const +{ + if(clip(x1, y1, x2, y2)) + return; + gl_line(clip_x1, clip_y1+((Svgalib_Video*)video)->displayoffset, clip_x2, clip_y2+((Svgalib_Video*)video)->displayoffset, c); +} + +void Svgalib_Video_bitmap::put_bitmap(const Bitmap& d, const int dx, + const int dy) const { + if(clip(dx, dy, d.width, d.height)) + return; + + if(d.width == d.realwidth) { + gl_putboxpart(clip_x1+pos_x, + clip_y1+pos_y+((Svgalib_Video*)video)->displayoffset, + clip_w, + clip_y2-clip_y1+1, + d.width, + d.height, + d[clip_y1-dy]+clip_x1-dx, + 0, 0); + } else { + int y; + for(y=clip_y1; y<=clip_y2; y++) + gl_putbox(clip_x1+pos_x, + y+((Svgalib_Video*)video)->displayoffset+pos_y, + clip_w, + 1, + d[y-dy]+clip_x1-dx); + } +} + +void Svgalib_Video_bitmap::put_sprite(const Sprite& d, const int dx, + const int dy) const { + int dx2 = dx - d.hot_x; + int dy2 = dy - d.hot_y; + + int phys_y; + + if(clip(dx2, dy2, d.width, d.height)) + return; + + phys_y=pos_y+((Svgalib_Video*)video)->displayoffset; + + if(clip_x1==dx2 && clip_y1==dy2 && + clip_w == d.width && clip_y2 == dy2+d.height-1) { + gl_putboxmask(clip_x1+pos_x, clip_y1+phys_y, clip_w, + clip_y2-clip_y1+1, d[0]); + } else { + int y; + for(y=clip_y1; y<=clip_y2; y++) + gl_putboxmask(clip_x1+pos_x, y+phys_y, clip_w, 1, + d[y-dy2]+clip_x1-dx2); + } +} + +void Svgalib_Video_bitmap::setmem() { +} + +Svgalib_Video* Svgalib_Video::New(int w, int h, int b, const char *wname) { + if(getenv("DISPLAY")) + return NULL; + else + return new Svgalib_Video(w, h, b, wname); +} + +Svgalib_Video::Svgalib_Video(int w, int h, int b, const char *wname) { + xwindow = false; + width = w; + height = h; + bit = b; + framecount = 0; + newpal = true; + need_paint = 2; + + vga_setmousesupport(1); + + if(vga_init() != 0) + (void)new Error("Fatal: error initializing SVGALib!\n"); + + if(vga_setmode(10 /* G640x480x256 */) != 0) + (void)new Error("Fatal: could not set 640x480x256 SVGA mode. Check SVGALib configuration\n"); + + gl_setcontextvga(10 /* G640x480x256 */); + + vb = Video_bitmap::New(0, 0, w, h); + + displayoffset = (vb->height); +} + +Svgalib_Video::~Svgalib_Video() { + if(vb) + delete vb; + + vga_setmode(0 /* TEXT */); +} + +void Svgalib_Video::lock() { + vb->setmem(); +} + +void Svgalib_Video::unlock() { +} + +void Svgalib_Video::flip() { + gl_setdisplaystart(0, displayoffset); + displayoffset = (vb->height) - displayoffset; + + vga_waitretrace(); + + if(newpal) { + pal.set(); + newpal = false; + } + + framecount++; +} + +void Svgalib_Video::setpal(const Palette& p) { + pal = p; + newpal=true; +} + +void Svgalib_Video::dosetpal(PALETTEENTRY pal[256], int size) { + int i; + gl_palette newpal; + + for(i = 0; i < size; i++) { + newpal.color[i].red = pal[i].peRed >> 2; + newpal.color[i].green = pal[i].peGreen >> 2; + newpal.color[i].blue = pal[i].peBlue >> 2; + } + gl_setpalettecolors(0, size, &newpal); +} + +void Svgalib_Video::start_frame() { + lock(); + if(cursor) { + cursor->put_back(); + cursor->move(); + } +} + +void Svgalib_Video::end_frame() { + if(cursor) { + cursor->get_back(); + cursor->draw(); + } + flip(); +} + +void Svgalib_Video::restore() { + newpal = true; + need_paint = 2; + /* FIXME: ne devrait pas être nécessaire, probablement lié au + trouble de signals dans Input_Svgalib. */ + vga_setmode(10 /* G640x480x256 */); +} + +void Svgalib_Video::clean_up() { +} + +void Svgalib_Video::snap_shot(int x, int y, int w, int h) { +} diff --git a/skelton/svgalib/video_x11.cpp b/skelton/svgalib/video_x11.cpp new file mode 100644 index 0000000..8bd0165 --- /dev/null +++ b/skelton/svgalib/video_x11.cpp @@ -0,0 +1,628 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#define Font XFont +#include +#include +#include +#undef Font +#include "debug.h" +#ifdef _DEBUG +#include "command.h" +#ifdef FPSMETER +#include "overmind.h" +#endif +#endif +#include "main.h" +#include "bitmap.h" +#include "sprite.h" +#include "video_x11.h" + +/* + * Gack! This is HORRIBLE! + */ +#include "quadra.xpm" + +Video_bitmap* Video_bitmap_X11::New(const int px, const int py, + const int w, const int h, const int rw) { + if(getenv("DISPLAY")) + return new Video_bitmap_X11(px, py, w, h, rw); + else + return NULL; +} + +Video_bitmap* Video_bitmap_X11::New(const int px, const int py, + const int w, const int h) { + if(getenv("DISPLAY")) + return new Video_bitmap_X11(px, py, w, h); + else + return NULL; +} + +Video_bitmap_X11::Video_bitmap_X11(const int px, const int py, + const int w, const int h, const int rw) { + width = w; + height = h; + pos_x = px; + pos_y = py; + fb = new Bitmap(NULL, w, h, rw); +} + +Video_bitmap_X11::Video_bitmap_X11(const int px, const int py, + const int w, const int h) { + width = w; + height = h; + pos_x = px; + pos_y = py; + fb = new Bitmap(NULL, w, h, video->pitch); +} + +Video_bitmap_X11::~Video_bitmap_X11() { + if(VERIFY(fb)) + delete fb; +} + +void Video_bitmap_X11::rect(const int x,const int y,const int w,const int h, + const int color) const { + int i; + char *bp; + + if(clip(x, y, w, h)) + return; + + clip_y1 += pos_y; + clip_y2 += pos_y; + clip_x1 += pos_x; + + i = 0; + for(bp = (char*) ((Video_X11*)video)->vfb + ((clip_y1) * video->vb->width); + bp <= (char*) ((Video_X11*)video)->vfb + ((clip_y2) * video->vb->width); + bp += video->vb->width) { + memset(&bp[clip_x1], color, clip_w); + } + + ((Video_X11*)video)->dirty(clip_x1, clip_y1, + clip_x1+clip_w-1, clip_y2); +} + +void Video_bitmap_X11::box(const int x,const int y,const int w,const int h, + const int color) const { + hline(y, x, w, color); + hline(y+h-1, x, w, color); + vline(x, y, h, color); + vline(x+w-1, y, h, color); +} + +void Video_bitmap_X11::get_bitmap(const Bitmap* bit, const int x, const int y, + const int w, const int h) const { + if(clip(x, y, w, h)) + return; + + Bitmap src((*fb)[clip_y1]+clip_x1, + clip_w, + clip_y2-clip_y1+1, + fb->realwidth); + + src.draw(*bit, clip_x1-x, clip_y1-y); + + ((Video_X11*)video)->dirty(clip_x1+pos_x, clip_y1+pos_y, + clip_x1+pos_x+clip_w, clip_y2+pos_y); +} + +void Video_bitmap_X11::put_pel(const int x, const int y, const Byte c) const { + fb->put_pel(x, y, c); + + ((Video_X11*)video)->dirty(x+pos_x, y+pos_y, + x+pos_x, y+pos_y); +} + +void Video_bitmap_X11::hline(const int y, const int x, + const int w, const Byte c) const { + fb->hline(y, x, w, c); + + ((Video_X11*)video)->dirty(x+pos_x, y+pos_y, + x+w+pos_x, y+pos_y); +} + +void Video_bitmap_X11::vline(const int x, const int y, + const int h, const Byte c) const { + fb->vline(x, y, h, c); + + ((Video_X11*)video)->dirty(x+pos_x, y+pos_y, + x+pos_x, y+h+pos_y); +} + +void Video_bitmap_X11::line(const int x1, const int y1, + const int x2, const int y2, + const Byte c) const { + fb->line(x1, y1, x2, y2, c); + + ((Video_X11*)video)->dirty(x1+pos_x, y1+pos_y, + x2+pos_x, y2+pos_y); +} + +void Video_bitmap_X11::put_bitmap(const Bitmap& d, + const int dx, const int dy) const { + d.draw(*fb, dx, dy); + + ((Video_X11*)video)->dirty(dx+pos_x, dy+pos_y, + dx+d.width+pos_x-1, dy+d.height+pos_y); +} + +void Video_bitmap_X11::put_sprite(const Sprite& d, + const int dx, const int dy) const { + d.draw(*fb, dx, dy); + + ((Video_X11*)video)->dirty(dx+pos_x, dy+pos_y, + dx+d.width+pos_x, dy+d.height+pos_y); +} + +void Video_bitmap_X11::setmem() { + if(fb) + fb->setmem((((Video_X11*)video)->vfb) + (pos_y * video->pitch) + pos_x); +} + +Video* Video_X11::New(int w, int h, int b, const char *wname) { + Display* display; + XVisualInfo visualinfo; + + if(!VERIFY(getenv("DISPLAY"))) + return NULL; + + display = XOpenDisplay(NULL); + + if(!VERIFY(display)) { + msgbox("Could not open [DISPLAY=%s]", getenv("DISPLAY")); + return NULL; + } + +#ifdef _DEBUG + if(command.token("sync")) { + skelton_msgbox("X11: synchronous communication with X server enabled.\n"); + XSynchronize(display, true); + } +#endif + + if(XMatchVisualInfo(display, + DefaultScreen(display), + 8, + PseudoColor, + &visualinfo)) { + return NEW(Video_X11_8, (w, h, b, wname, display, visualinfo.visual)); + }; + + if(XMatchVisualInfo(display, + DefaultScreen(display), + 16, + TrueColor, + &visualinfo)) { + return NEW(Video_X11_16, (w, h, b, wname, display, visualinfo.visual, 16)); + }; + + if(XMatchVisualInfo(display, + DefaultScreen(display), + 15, + TrueColor, + &visualinfo)) { + return NEW(Video_X11_16, (w, h, b, wname, display, visualinfo.visual, 15)); + }; + + if(XMatchVisualInfo(display, + DefaultScreen(display), + 24, + TrueColor, + &visualinfo)) { + return NEW(Video_X11_24, (w, h, b, wname, display, visualinfo.visual)); + }; + + msgbox("X11: Could not find any compatible visual.\n"); + + return NULL; +} + +static volatile bool quit = false; + +static void sigint_handler(int sig) { + msgbox("SIGINT caught (probably ctrl-c), exiting.\n"); + quit = true; +} + +static bool xerror = false; +static XErrorHandler xoldhandler = 0; + +static int xhandler(Display* display, XErrorEvent* error) { + xerror = true; + return 0; +} + +Video_X11::Video_X11(int w, int h, int b, + const char *wname, + Display* dpy, + Visual* vis, + int d): + display(NULL), + image(NULL), + vfb(NULL), + min_x2(w), max_x2(0), min_y2(h), max_y2(0), + window(0), + visual(NULL), + delete_win(0), + depth(d), + do_shm(false) { + /* NOTE: ont assume que "b" est toujours 8, même si ont essaye de + faire le smart à des places (comme "bit = b"). */ + + XSetWindowAttributes attribs; + XGCValues gcvalues; + XClassHint *classhint; + XWMHints *wmhints; + XSizeHints sizehints; + Pixmap ico_pixmap; + Pixmap ico_mask; + char *argv[1]; + int tmp_y; + + setuid(getuid()); + setgid(getgid()); + seteuid(getuid()); + setegid(getgid()); + + xwindow = true; + + for(tmp_y = 0; tmp_y < 480; tmp_y++) { + min_x[tmp_y] = w; + max_x[tmp_y] = 0; + }; + + width = w; + height = h; + bit = b; + pitch = w; + framecount = 0; + newpal = true; + need_paint = 2; + + signal(SIGINT, sigint_handler); + + display = dpy; + visual = vis; + + attribs.event_mask = ExposureMask + | KeyPressMask + | KeyReleaseMask + | ButtonPressMask + | ButtonReleaseMask + | LeaveWindowMask + | FocusChangeMask + | PointerMotionMask; + + attribs.border_pixel = 0; + + attribs.colormap = DefaultColormap(display, DefaultScreen(display)); + + window = XCreateWindow(display, + DefaultRootWindow(display), + 0, 0, /* X, Y relative to parent window */ + w, h, /* width, height */ + 0, /* border width */ + depth, /* color depth */ + InputOutput, + visual, + CWEventMask | CWBorderPixel | CWColormap, + &attribs); + + if(!window) + (void)new Error("XCreateWindow failed"); + + classhint = XAllocClassHint(); + classhint->res_name = "LudusSkelton"; + classhint->res_class = (char*)wname; + XSetClassHint(display, window, classhint); + XFree(classhint); + + argv[0] = "/usr/games/quadra"; + XSetCommand(display, window, argv, 1); + + XpmCreatePixmapFromData(display, + DefaultRootWindow(display), + quadra_xpm, + &ico_pixmap, + &ico_mask, + NULL); + + wmhints = XAllocWMHints(); + wmhints->flags = WindowGroupHint | IconPixmapHint | IconMaskHint; + wmhints->icon_pixmap = ico_pixmap; + wmhints->icon_mask = ico_mask; + wmhints->window_group = window; + XSetWMHints(display, window, wmhints); + XFree(wmhints); + + sizehints.flags = PMinSize | PMaxSize | PBaseSize; + sizehints.min_width = w; + sizehints.min_height = h; + sizehints.max_width = w; + sizehints.max_height = h; + sizehints.base_width = w; + sizehints.base_height = h; + XSetWMNormalHints(display, window, &sizehints); + + delete_win = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, window, &delete_win, 1); + + XStoreName(display, window, wname); + XSetIconName(display, window, wname); + + gcvalues.graphics_exposures = False; + + gc = XCreateGC(display, + window, + GCGraphicsExposures, + &gcvalues); + + if(!gc) + (void)new Error("XCreateGC failed"); + + XMapRaised(display, window); + + while(1) { + XEvent event; + + XNextEvent(display, &event); + + if((event.type == Expose) && (event.xexpose.count == 0)) + break; + } + + do { + if(!XShmQueryExtension(display)) + break; + + msgbox("Using MIT-SHM extension\n"); + do_shm = true; + } while(0); + + image = NULL; + + if(do_shm) { + image = XShmCreateImage(display, + visual, + depth, /* color depth */ + ZPixmap, + NULL, + &shminfo, + w, h); + + if(!image) + (void)new Error("XCreateImage failed"); + + shminfo.shmid = shmget(IPC_PRIVATE, + image->height * image->bytes_per_line, + IPC_CREAT|0777); + + if(shminfo.shmid == -1) + (void)new Error("XShm: shmget failed"); + + shminfo.shmaddr = image->data = (char*)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = False; + + shmctl(shminfo.shmid, IPC_RMID, 0); + + /* voodoo warning: XShmAttach foire horriblement si le serveur X + n'est pas local. Soyons futés. */ + XSync(display, False); + xerror = false; + if(xoldhandler != xhandler) + xoldhandler = XSetErrorHandler(xhandler); + + if(!XShmAttach(display, &shminfo)) + (void)new Error("XShm: XShmAttach failed"); + + XSync(display, False); + if(xoldhandler) + XSetErrorHandler(xoldhandler); + xoldhandler = 0; + + if(xerror) { + do_shm = false; + XDestroyImage(image); + XSync(display, False); + shmdt(shminfo.shmaddr); + image = 0; + } + } + + if(!image) { + image = XCreateImage(display, + visual, + depth, /* color depth */ + ZPixmap, + 0, + NULL, + w, h, + 8, + 0); + + if(!image) + (void)new Error("XCreateImage failed"); + + image->data = (char*)malloc(image->height * image->bytes_per_line); + + if(!image->data) + (void)new Error("Out of memory"); + } + + vb = Video_bitmap::New(0, 0, w, h, w); + + if(!vb) + (void)new Error("Couldn't create a video bitmap."); +} + +Video_X11::~Video_X11() { + if(vb) + delete vb; + + if(do_shm) { + image->data = NULL; + } + + if(image) + XDestroyImage(image); + + if(gc) + XFreeGC(display, gc); + + if(window) { + XUnmapWindow(display, window); + XDestroyWindow(display, window); + } + + XCloseDisplay(display); +} + +void Video_X11::dirty(int x1, int y1, int x2, int y2) { + short y; + + dirty2(x1, y1, x2, y2); + + if(y1 < 0) + y1 = 0; + if(y2 > 479) + y2 = 479; + + for(y = y1; y <= y2; y++) { + if(x1 < min_x[y]) { + min_x[y] = x1; + if(min_x[y] < 0) + min_x[y] = 0; + } + + if(x2 > max_x[y]) { + max_x[y] = x2; + if(max_x[y] >= vb->width) + max_x[y] = vb->width - 1; + } + } +}; + +void Video_X11::dirty2(int x1, int y1, int x2, int y2) { + if(x1 < min_x2) { + min_x2 = x1; + if(min_x2 < 0) + min_x2 = 0; + } + + if(x2 > max_x2) { + max_x2 = x2; + if(max_x2 >= vb->width) + max_x2 = vb->width - 1; + } + + if(y1 < min_y2) { + min_y2 = y1; + if(min_y2 < 0) + min_y2 = 0; + } + + if(y2 > max_y2) { + max_y2 = y2; + if(max_y2 >= vb->height) + max_y2 = vb->height - 1; + } +}; + +void Video_X11::lock() { + if(vb) + vb->setmem(); +} + +void Video_X11::unlock() { + skelton_msgbox("Unimplemented: Video_X11::unlock\n"); +} + +void Video_X11::start_frame() { + if(quit) + quit_game(); + + lock(); +} + +void Video_X11::flip() { + sleep(0); + + if(max_x2 > min_x2) { + /* des fois que la frame précédente soit pas finie */ + XSync(display, False); + + if(do_shm) + XShmPutImage(display, + window, + gc, + image, + min_x2, min_y2, /* src x, y */ + min_x2, min_y2, /* dest x, y */ + max_x2-min_x2, max_y2-min_y2, + false); + else + XPutImage(display, + window, + gc, + image, + min_x2, min_y2, /* src x, y */ + min_x2, min_y2, /* dest x, y */ + max_x2-min_x2, max_y2-min_y2); + + /* pour que tout passe */ + XFlush(display); + + /* reset le dirty rect */ + min_x2 = vb->width; + min_y2 = vb->height; + max_x2 = 0; + max_y2 = 0; + } +} + +void Video_X11::end_frame() { +#ifdef FPSMETER + static unsigned int lastupdate = 0; + static unsigned int lastframe = 0; + static char st[100]; + + if(ecran && ecran->font) { + lastframe = getmsec()-lastupdate; + if(lastframe) + sprintf(st, "%i", 1000 / (getmsec()-lastupdate)); + else + sprintf(st, "high"); + video->vb->rect(0, 0, 50, 20, 0); + ecran->font->draw(st, video->vb, 0, 0); + lastupdate = getmsec(); + } +#endif + + flip(); +} + +void Video_X11::setpal(const Palette& p) { + pal = p; + newpal = true; +} + +void Video_X11::restore() { + newpal = true; + need_paint = 2; +} + +void Video_X11::clean_up() { +} + +void Video_X11::snap_shot(int x, int y, int w, int h) { + skelton_msgbox("Unimplemented: Video_X11::snap_shot\n"); +} diff --git a/skelton/svgalib/video_x11_16.cpp b/skelton/svgalib/video_x11_16.cpp new file mode 100644 index 0000000..625e7e8 --- /dev/null +++ b/skelton/svgalib/video_x11_16.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#define Font XFont +#include +#include +#include +#undef Font +#include "debug.h" +#include "main.h" +#include "bitmap.h" +#include "sprite.h" +#include "video_x11.h" + +Video_X11_16::Video_X11_16(int w, int h, int b, + const char *wname, + Display* dpy, + Visual* vis, + int dp): + Video_X11(w, h, b, wname, dpy, vis, dp) { + vfb = (unsigned char*)malloc(w * h); + + if(!vfb) + (void)new Error("Could not allocate virtual frame buffer."); +} + +Video_X11_16::~Video_X11_16() { + if(vfb) + free(vfb); +} + +void Video_X11_16::flip() { + unsigned short* buf; + int x, y; + + if(newpal) { + pal.set(); + newpal = false; + } + + if(max_x > min_x) { + // Conversion du buffer 8 bpp vers un buffer 16 bpp. + buf = (unsigned short*) image->data; + for(y = 0; y < 480; y++) + for(x = min_x[y]; x <= max_x[y]; x++) { + buf[(y * width) + x] = colors[vfb[(y * width) + x]]; + } + + /* reset le dirty rect */ + for(y = 0; y < 480; y++) { + min_x[y] = vb->width; + max_x[y] = 0; + } + } + + Video_X11::flip(); + + framecount++; +} + +void Video_X11_16::dosetpal(PALETTEENTRY pal[256], int size) { + XColor color; + int i; + + color.flags = DoRed|DoGreen|DoBlue; + + for(i = 0; i < size; i++) { + color.red = (pal[i].peRed << 8) + pal[i].peRed; + color.green = (pal[i].peGreen << 8) + pal[i].peGreen; + color.blue = (pal[i].peBlue << 8) + pal[i].peBlue; + + XAllocColor(display, + DefaultColormap(display, DefaultScreen(display)), + &color); + + colors[i] = color.pixel; + } + + dirty(0, 0, width-1, height-1); +} diff --git a/skelton/svgalib/video_x11_24.cpp b/skelton/svgalib/video_x11_24.cpp new file mode 100644 index 0000000..05a93bc --- /dev/null +++ b/skelton/svgalib/video_x11_24.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#define Font XFont +#include +#include +#include +#undef Font +#include "debug.h" +#include "main.h" +#include "bitmap.h" +#include "sprite.h" +#include "video_x11.h" + +Video_X11_24::Video_X11_24(int w, int h, int b, + const char *wname, + Display* dpy, + Visual* vis): + Video_X11(w, h, b, wname, dpy, vis, 24) { + vfb = (unsigned char*)malloc(w * h); + + if(!vfb) + (void)new Error("Could not allocate virtual frame buffer."); +} + +Video_X11_24::~Video_X11_24() { + if(vfb) + free(vfb); +} + +void Video_X11_24::flip() { + +#pragma pack(1) + struct Pixel3 { + unsigned char red, green, blue; + }; + + struct PixelValue { + Pixel3 pixel; + unsigned char pad; + }; +#pragma pack() + + unsigned long* buf32; + Pixel3* buf24; + int x, y; + + if(newpal) { + pal.set(); + newpal = false; + } + + if(max_x > min_x) { + if(image->bits_per_pixel == 24) { + /* Conversion 8 bpp to 24bpp. */ + buf24 = (Pixel3*) image->data; + for(y = 0; y < 480; y++) + for(x = min_x[y]; x <= max_x[y]; x++) + buf24[(y * width) + x] = + ((PixelValue*)(&colors[vfb[(y * width) +x]]))->pixel; + } else { + /* Conversion du buffer 8 bpp vers un buffer 32 bpp. */ + buf32 = (unsigned long*) image->data; + for(y = 0; y < 480; y++) + for(x = min_x[y]; x <= max_x[y]; x++) + buf32[(y * width) + x] = + colors[vfb[(y * width) + x]]; + } + + /* reset le dirty rect */ + for(y = 0; y < 480; y++) { + min_x[y] = vb->width; + max_x[y] = 0; + } + } + + Video_X11::flip(); + + framecount++; +} + +void Video_X11_24::dosetpal(PALETTEENTRY pal[256], int size) { + int i; + + for(i = 0; i < size; i++) { + colors[i] = pal[i].peRed << 16; + colors[i] += pal[i].peGreen << 8; + colors[i] += pal[i].peBlue; + } + + dirty(0, 0, width-1, height-1); +} diff --git a/skelton/svgalib/video_x11_8.cpp b/skelton/svgalib/video_x11_8.cpp new file mode 100644 index 0000000..e7bc06a --- /dev/null +++ b/skelton/svgalib/video_x11_8.cpp @@ -0,0 +1,75 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#define Font XFont +#include +#include +#include +#undef Font +#include "debug.h" +#include "main.h" +#include "bitmap.h" +#include "sprite.h" +#include "video_x11.h" + +Video_X11_8::Video_X11_8(int w, int h, int b, + const char *wname, + Display* dpy, + Visual* vis): + Video_X11(w, h, b, wname, dpy, vis, 8) { + int i; + + vfb = (unsigned char*) image->data; + + cmap = XCreateColormap(display, + window, + visual, + AllocAll); + + for(i = 0; i < 256; i++) { + colors[i].pixel = i; + colors[i].flags = DoRed | DoGreen | DoBlue; + } + + XSetWindowColormap(display, window, cmap); +} + +Video_X11_8::~Video_X11_8() { + if(cmap) + XFreeColormap(display, cmap); +} + +void Video_X11_8::flip() { + short y; + + if(newpal) { + pal.set(); + newpal = false; + } + + /* reset le dirty rect */ + for(y = 0; y < 480; y++) { + min_x[y] = vb->width; + max_x[y] = 0; + } + + Video_X11::flip(); + + framecount++; +} + +void Video_X11_8::dosetpal(PALETTEENTRY pal[256], int size) { + int i; + + for(i = 0; i < size; i++) { + colors[i].red = (pal[i].peRed << 8) + pal[i].peRed; + colors[i].green = (pal[i].peGreen << 8) + pal[i].peGreen; + colors[i].blue = (pal[i].peBlue << 8) + pal[i].peBlue; + } + + XStoreColors(display, cmap, colors, size); +} diff --git a/skelton/test/Makefile b/skelton/test/Makefile new file mode 100644 index 0000000..5eeac27 --- /dev/null +++ b/skelton/test/Makefile @@ -0,0 +1,20 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +.PHONY: local + +local: + $(MAKE) -C .. + +test: testmain.o -lugs + $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +string: string.o -lugs + $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +sound: sound.o -lugs + $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +dynlib: dynlib.o -lugs + $(LINK.cc) $^ $(LOADLIBES) -ldl $(LDLIBS) -o $@ + diff --git a/skelton/test/bubble2.wav b/skelton/test/bubble2.wav new file mode 100644 index 0000000..cc88fe0 Binary files /dev/null and b/skelton/test/bubble2.wav differ diff --git a/skelton/test/cursor.raw b/skelton/test/cursor.raw new file mode 100644 index 0000000..ac0a76b Binary files /dev/null and b/skelton/test/cursor.raw differ diff --git a/skelton/test/dynlib.cpp b/skelton/test/dynlib.cpp new file mode 100644 index 0000000..c2093d2 --- /dev/null +++ b/skelton/test/dynlib.cpp @@ -0,0 +1,33 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "dynlib.h" +#include + +void start_game() { + DynLib *dlobj; + void *test; + + dlobj = new DynLib("libz.so"); + + if(!dlobj->openLibrary()) + printf("openLibrary() error: %s\n", dlobj->getError()); + + test = dlobj->getSymbol("foo"); + if(!test) { + printf("getSymbol() error: %s\n", dlobj->getError()); + } + + test = dlobj->getSymbol("zlibVersion"); + if(!test) { + printf("getSymbol() error: %s\n", dlobj->getError()); + } + + /* this step is optional, the destructor calls closeLibrary */ + dlobj->closeLibrary(); + + delete dlobj; +} diff --git a/skelton/test/ptestsound b/skelton/test/ptestsound new file mode 100755 index 0000000..cb37d2e --- /dev/null +++ b/skelton/test/ptestsound @@ -0,0 +1,34 @@ +#! /usr/bin/perl + +use Fcntl; + +my($snddata, $index, $sndbuf, $sndsize, $chunksize, $mousse, $thres); + +$chunksize = 16384; +$index = 0; +$thres = 2250; +$mousse = $thres; + +open(SOUND, " +#include +//#include +#include "sound.h" +//#include "main.h" + +/* Ceci est un test */ + +void start_game() { + Sample* thesample; + Res_dos* foo; + +// video = Video::New(640, 480, 8, "Quadra"); +// input = Input::New(); +// music = Music::alloc(); + mcheck(NULL); + sound = new Sound(); + if(sound->active == false) + printf("Sound NOT active!\n"); + printf("Loading ../sons/glissup.wav\n"); + foo = new Res_dos("../sons/glissup.wav", RES_READ); + thesample = new Sample(*foo, 2); + int pa = 0; + int freq = 22050; + Sfx stmp(thesample, 0, 0, pa, freq); + do { + sound->process(); + //start_frame(); + //video->vb->rect(0, 0, 640, 480, 15); + //end_frame(); + } while(1); + + sound->delete_sample(thesample); + delete thesample; + delete foo; + +#if 0 + if(video) + delete video; +#endif + + printf("Terminated, sample deleted.\nPress ESC to quit.\n"); +} + diff --git a/skelton/test/string.cpp b/skelton/test/string.cpp new file mode 100644 index 0000000..a66bf71 --- /dev/null +++ b/skelton/test/string.cpp @@ -0,0 +1,30 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "res.h" +#include "resfile.h" +#include "stringtable.h" +#include "../include/texte.h" + +/* Ceci est un test pour Res_doze et stringtable */ + +void start_game() { + int i; + +#ifdef UGS_LINUX + resfile = new Resfile("../source/quadra.res"); +#endif + stringtable = new Stringtable("texte.txt"); + + i = 1; + while(i <= 363) { + printf("%3i - %s\n", i, ST_BASE(i)); + i++; + } + +} + diff --git a/skelton/test/testmain.cpp b/skelton/test/testmain.cpp new file mode 100644 index 0000000..3074620 --- /dev/null +++ b/skelton/test/testmain.cpp @@ -0,0 +1,68 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "input.h" +#include "video.h" +#include "cursor.h" +#include "res.h" +#include "pcx.h" + +/* Ceci est un test */ + + +void start_game() { + Res_dos foo("testpic.pcx", RES_READ); + Pcx foopic(foo); + Bitmap bit(foopic); + int i = 0; + Sprite *cur; + { + Res_dos res("cursor.raw"); + Raw raw(res); + Bitmap bit(raw); + cur = new Sprite(bit); + } + + video=new Video(640, 480, 8, "Test pour Linux"); + input=new Input(); + cursor=new Cursor(cur); + // cursor->restore_back(false); + Palette pal(foopic); + video->setpal(pal); + Video_bitmap v(40, 40, 100, 100); + + video->start_frame(); + video->vb->put_bitmap(bit, 0, 0); + video->end_frame(); + + video->start_frame(); + video->vb->put_bitmap(bit, 0, 0); + video->end_frame(); + + while(input->quel_key == -1) { + input->check(); + video->start_frame(); + v.setmem(); + // video->vb->rect(0,0,640,480,0); + video->vb->hline(38, 38, 104, 100); + video->vb->hline(141, 38, 104, 100); + video->vb->vline(38, 38, 104, 100); + video->vb->vline(141, 38, 104, 100); + v.hline(0, 0, 100, 100); + v.hline(99, 0, 100, 100); + v.vline(0, 0, 100, 100); + v.vline(99, 0, 100, 100); + // v.rect(0,0,40,40,1); + i++; + if(i == 600) + i = 0; + video->end_frame(); + } + + delete cur; +} + diff --git a/skelton/test/testpic.pcx b/skelton/test/testpic.pcx new file mode 100644 index 0000000..464b109 Binary files /dev/null and b/skelton/test/testpic.pcx differ diff --git a/skelton/tools/.cvsignore b/skelton/tools/.cvsignore new file mode 100644 index 0000000..cd56cd7 --- /dev/null +++ b/skelton/tools/.cvsignore @@ -0,0 +1 @@ +dumpwad diff --git a/skelton/tools/dir.mk b/skelton/tools/dir.mk new file mode 100644 index 0000000..7e0a95e --- /dev/null +++ b/skelton/tools/dir.mk @@ -0,0 +1,24 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +ifndef MAKEFILE_INCLUDED + +.PHONY: local + +local: + $(MAKE) -C .. + +else +ifndef RULES + +TARGETS+=tools/dumpwad +CLEANS+=tools/dumpwad + +else + +tools/dumpwad: tools/dumpwad.o lib/libugs.a + $(LINK.cc) -o $@ $^ + +endif +include tools/wadder/dir.mk +endif diff --git a/skelton/tools/dumpdb b/skelton/tools/dumpdb new file mode 100755 index 0000000..acd8417 --- /dev/null +++ b/skelton/tools/dumpdb @@ -0,0 +1,12 @@ +#! /usr/bin/perl + +use DB_File; + +my(%db); + +tie(%db, 'DB_File', $ARGV[0]); + +foreach $foo (keys %db) { + print "$foo -> $db{$foo}\n"; +} + diff --git a/skelton/tools/dumpwad.cpp b/skelton/tools/dumpwad.cpp new file mode 100644 index 0000000..db7aafb --- /dev/null +++ b/skelton/tools/dumpwad.cpp @@ -0,0 +1,33 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "resfile.h" + +char *usage = "incorrect number of parameters\n"; + +int main(int ARGC, char **ARGV, char **ENV) { + Resfile *wad; + + if(ARGC != 2) { + fprintf(stderr, "%s: %s", ARGV[0], usage); + exit(1); + } + + wad = new Resfile(ARGV[1]); + + delete wad; + + return 0; +} diff --git a/skelton/tools/wadder/.cvsignore b/skelton/tools/wadder/.cvsignore new file mode 100644 index 0000000..f0df2d8 --- /dev/null +++ b/skelton/tools/wadder/.cvsignore @@ -0,0 +1 @@ +wadder diff --git a/skelton/tools/wadder/dir.mk b/skelton/tools/wadder/dir.mk new file mode 100644 index 0000000..86e63d2 --- /dev/null +++ b/skelton/tools/wadder/dir.mk @@ -0,0 +1,24 @@ +# Makefile pour le Universal Game Skelton +# $Id$ + +ifndef MAKEFILE_INCLUDED + +.PHONY: local + +local: + $(MAKE) -C ../.. + +else +ifndef RULES + +TARGETS+=tools/wadder/wadder +CLEANS+=tools/wadder/wadder + +else + +tools/wadder/wadder: tools/wadder/wadder.o lib/libugs.a + $(LINK.cc) -o $@ $^ + +endif +endif + diff --git a/skelton/tools/wadder/wadder.cpp b/skelton/tools/wadder/wadder.cpp new file mode 100644 index 0000000..3dc8ea5 --- /dev/null +++ b/skelton/tools/wadder/wadder.cpp @@ -0,0 +1,76 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "stringtable.h" +#include "res.h" + +char *usage = "insufficient number of parameters\n"; +Resfile *wad; + +char *basename(const char* f) { + char* p=(char*)(f+strlen(f)); + while(*p != '/' && *p != '\\' && p>=f) + p--; + return p+1; +} + +void addfile(const char* fname) { + Res_dos *res; + int resnamelen; + char *data; + + printf("%s: ", fname); + + res = new Res_dos(fname, RES_TRY); + data = new char[res->size()]; + res->read(data, res->size()); + + resnamelen = strlen(basename(fname))+1; + + wad->add(basename(fname), res->size(), data); + + delete res; + delete data; + + printf("done\n"); +} + +int main(int ARGC, char **ARGV, char **ENV) { + Res_dos *res; + Byte* data; + + if(ARGC < 3) { + fprintf(stderr, "%s: %s", ARGV[0], usage); + exit(1); + } + + wad = new Resfile(ARGV[1], false); + + wad->clear(); + + res = new Res_dos(ARGV[2], RES_READ); + data = new Byte[res->size()+1]; + + memcpy(data, res->buf(), res->size()); + + Stringtable st(data, res->size()); + + for(int i=0; ifreeze(); + + delete wad; + + return 0; +} + +void start_game(void) { +} diff --git a/sons/blip1.wav b/sons/blip1.wav new file mode 100644 index 0000000..5b9840a Binary files /dev/null and b/sons/blip1.wav differ diff --git a/sons/bloop.wav b/sons/bloop.wav new file mode 100644 index 0000000..8580d5a Binary files /dev/null and b/sons/bloop.wav differ diff --git a/sons/bubble2.wav b/sons/bubble2.wav new file mode 100644 index 0000000..cc88fe0 Binary files /dev/null and b/sons/bubble2.wav differ diff --git a/sons/ceramic3.wav b/sons/ceramic3.wav new file mode 100644 index 0000000..4b032e2 Binary files /dev/null and b/sons/ceramic3.wav differ diff --git a/sons/chop2.wav b/sons/chop2.wav new file mode 100644 index 0000000..cc9ca31 Binary files /dev/null and b/sons/chop2.wav differ diff --git a/sons/clang3.wav b/sons/clang3.wav new file mode 100644 index 0000000..5475b04 Binary files /dev/null and b/sons/clang3.wav differ diff --git a/sons/click01.wav b/sons/click01.wav new file mode 100644 index 0000000..f319003 Binary files /dev/null and b/sons/click01.wav differ diff --git a/sons/click_1.wav b/sons/click_1.wav new file mode 100644 index 0000000..37e3272 Binary files /dev/null and b/sons/click_1.wav differ diff --git a/sons/click_3.wav b/sons/click_3.wav new file mode 100644 index 0000000..32b42b6 Binary files /dev/null and b/sons/click_3.wav differ diff --git a/sons/corkpop.wav b/sons/corkpop.wav new file mode 100644 index 0000000..68df473 Binary files /dev/null and b/sons/corkpop.wav differ diff --git a/sons/cuckoo.wav b/sons/cuckoo.wav new file mode 100644 index 0000000..bb60afd Binary files /dev/null and b/sons/cuckoo.wav differ diff --git a/sons/explod03.wav b/sons/explod03.wav new file mode 100644 index 0000000..370999c Binary files /dev/null and b/sons/explod03.wav differ diff --git a/sons/explod05.wav b/sons/explod05.wav new file mode 100644 index 0000000..9bf88bb Binary files /dev/null and b/sons/explod05.wav differ diff --git a/sons/explod06.wav b/sons/explod06.wav new file mode 100644 index 0000000..d45943b Binary files /dev/null and b/sons/explod06.wav differ diff --git a/sons/fadeout.wav b/sons/fadeout.wav new file mode 100644 index 0000000..cbcdf02 Binary files /dev/null and b/sons/fadeout.wav differ diff --git a/sons/glass01.wav b/sons/glass01.wav new file mode 100644 index 0000000..5438652 Binary files /dev/null and b/sons/glass01.wav differ diff --git a/sons/glass03.wav b/sons/glass03.wav new file mode 100644 index 0000000..16abded Binary files /dev/null and b/sons/glass03.wav differ diff --git a/sons/glass04.wav b/sons/glass04.wav new file mode 100644 index 0000000..ef4a1cb Binary files /dev/null and b/sons/glass04.wav differ diff --git a/sons/glissup.wav b/sons/glissup.wav new file mode 100644 index 0000000..213445b Binary files /dev/null and b/sons/glissup.wav differ diff --git a/sons/handbell.wav b/sons/handbell.wav new file mode 100644 index 0000000..9df7440 Binary files /dev/null and b/sons/handbell.wav differ diff --git a/sons/hitwood1.wav b/sons/hitwood1.wav new file mode 100644 index 0000000..30dfa51 Binary files /dev/null and b/sons/hitwood1.wav differ diff --git a/sons/hooter03.wav b/sons/hooter03.wav new file mode 100644 index 0000000..2f8f2de Binary files /dev/null and b/sons/hooter03.wav differ diff --git a/sons/knock.wav b/sons/knock.wav new file mode 100644 index 0000000..3c99928 Binary files /dev/null and b/sons/knock.wav differ diff --git a/sons/metal1.wav b/sons/metal1.wav new file mode 100644 index 0000000..3fb67f7 Binary files /dev/null and b/sons/metal1.wav differ diff --git a/sons/metal3.wav b/sons/metal3.wav new file mode 100644 index 0000000..dc5b1fb Binary files /dev/null and b/sons/metal3.wav differ diff --git a/sons/metal6.wav b/sons/metal6.wav new file mode 100644 index 0000000..fb0fb46 Binary files /dev/null and b/sons/metal6.wav differ diff --git a/sons/pop1.wav b/sons/pop1.wav new file mode 100644 index 0000000..e39cdac Binary files /dev/null and b/sons/pop1.wav differ diff --git a/sons/potato_get.wav b/sons/potato_get.wav new file mode 100644 index 0000000..67a5284 Binary files /dev/null and b/sons/potato_get.wav differ diff --git a/sons/pwap2.wav b/sons/pwap2.wav new file mode 100644 index 0000000..33544d4 Binary files /dev/null and b/sons/pwap2.wav differ diff --git a/sons/smash2.wav b/sons/smash2.wav new file mode 100644 index 0000000..97670b1 Binary files /dev/null and b/sons/smash2.wav differ diff --git a/sons/splodge.wav b/sons/splodge.wav new file mode 100644 index 0000000..5d4c1c5 Binary files /dev/null and b/sons/splodge.wav differ diff --git a/sons/spring.wav b/sons/spring.wav new file mode 100644 index 0000000..9f9b101 Binary files /dev/null and b/sons/spring.wav differ diff --git a/sons/tapdrip.wav b/sons/tapdrip.wav new file mode 100644 index 0000000..3a8ae8c Binary files /dev/null and b/sons/tapdrip.wav differ diff --git a/sons/w_bayo_0.wav b/sons/w_bayo_0.wav new file mode 100644 index 0000000..ba2b9b5 Binary files /dev/null and b/sons/w_bayo_0.wav differ diff --git a/sons/water05_1.wav b/sons/water05_1.wav new file mode 100644 index 0000000..ba844ae Binary files /dev/null and b/sons/water05_1.wav differ diff --git a/sons/water05_2.wav b/sons/water05_2.wav new file mode 100644 index 0000000..4ea060b Binary files /dev/null and b/sons/water05_2.wav differ diff --git a/sons/water05_3.wav b/sons/water05_3.wav new file mode 100644 index 0000000..e110cd3 Binary files /dev/null and b/sons/water05_3.wav differ diff --git a/sons/whistle1.wav b/sons/whistle1.wav new file mode 100644 index 0000000..d9c57d4 Binary files /dev/null and b/sons/whistle1.wav differ diff --git a/sons/whistle2.wav b/sons/whistle2.wav new file mode 100644 index 0000000..8ada4ec Binary files /dev/null and b/sons/whistle2.wav differ diff --git a/sons/whizz1.wav b/sons/whizz1.wav new file mode 100644 index 0000000..09d3a3f Binary files /dev/null and b/sons/whizz1.wav differ diff --git a/sons/zingle.wav b/sons/zingle.wav new file mode 100644 index 0000000..240dd24 Binary files /dev/null and b/sons/zingle.wav differ diff --git a/source/.cvsignore b/source/.cvsignore new file mode 100644 index 0000000..238443c --- /dev/null +++ b/source/.cvsignore @@ -0,0 +1,2 @@ +quadra +quadra.res diff --git a/source/bloc.cpp b/source/bloc.cpp new file mode 100644 index 0000000..6643e05 --- /dev/null +++ b/source/bloc.cpp @@ -0,0 +1,99 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "color.h" +#include "quadra.h" +#include "bloc.h" + +Bloc::Bloc(int q, int c, int px, int py) { + quel=q; + if(c != -1) + col=c; + else + col=quel; + bx=px; + by=py; + calc_xy(); + y -= 17<<4; // pour forcer le bloc a commencer au debut de sa + // position initiale + rot=0; +} + +void Bloc::draw(const Video_bitmap* b, int tx, int ty) const { + int i,j; + Byte t, to[4]; + if(tx == -1) + tx = x>>4; + if(ty == -1) + ty = y>>4; + for(j=0; j<4; j++) + for(i=0; i<4; i++) { + t = bloc[quel][rot][j][i]; + if(t) { + if(i > 0) + to[0] = bloc[quel][rot][j][i-1]; + else + to[0] = 0; + if(j > 0) + to[1] = bloc[quel][rot][j-1][i]; + else + to[1] = 0; + if(i < 3) + to[2] = bloc[quel][rot][j][i+1]; + else + to[2] = 0; + if(j < 3) + to[3] = bloc[quel][rot][j+1][i]; + else + to[3] = 0; + raw_draw_bloc_corner(b, tx+i*18, ty+j*18, t&15, color[col], to); + } + } +} + +void Bloc::small_draw(const Video_bitmap* b, int tx, int ty) const { + int i,j; + Byte t; + if(tx == -1) + tx = x>>4; + if(ty == -1) + ty = y>>4; + for(j=0; j<4; j++) + for(i=0; i<4; i++) { + t = bloc[quel][rot][j][i]; + if(t) + raw_small_draw_bloc(b, tx+i*6, ty+j*6, t&15, color[col]); + } +} + +const Byte Bloc::bloc[7][4][4][4]={{{{0,0,0,0},{0,3,6,0},{0,9,12,0},{0,0,0,0}}, + {{0,0,0,0},{0,3,6,0},{0,9,12,0},{0,0,0,0}}, + {{0,0,0,0},{0,3,6,0},{0,9,12,0},{0,0,0,0}}, + {{0,0,0,0},{0,3,6,0},{0,9,12,0},{0,0,0,0}}}, + {{{0,0,0,0},{0,3,14,0},{11,12,0,0},{0,0,0,0}}, + {{7,0,0,0},{9,6,0,0},{0,13,0,0},{0,0,0,0}}, + {{0,0,0,0},{0,3,14,0},{11,12,0,0},{0,0,0,0}}, + {{7,0,0,0},{9,6,0,0},{0,13,0,0},{0,0,0,0}}}, + {{{0,0,0,0},{11,6,0,0},{0,9,14,0},{0,0,0,0}}, + {{0,7,0,0},{3,12,0,0},{13,0,0,0},{0,0,0,0}}, + {{0,0,0,0},{11,6,0,0},{0,9,14,0},{0,0,0,0}}, + {{0,7,0,0},{3,12,0,0},{13,0,0,0},{0,0,0,0}}}, + {{{0,0,0,0},{3,10,14,0},{13,0,0,0},{0,0,0,0}}, + {{11,6,0,0},{0,5,0,0},{0,13,0,0},{0,0,0,0}}, + {{0,0,7,0},{11,10,12,0},{0,0,0,0},{0,0,0,0}}, + {{0,7,0,0},{0,5,0,0},{0,9,14,0},{0,0,0,0}}}, + {{{0,0,0,0},{11,10,6,0},{0,0,13,0},{0,0,0,0}}, + {{0,7,0,0},{0,5,0,0},{11,12,0,0},{0,0,0,0}}, + {{7,0,0,0},{9,10,14,0},{0,0,0,0},{0,0,0,0}}, + {{0,3,14,0},{0,5,0,0},{0,13,0,0},{0,0,0,0}}}, + {{{0,0,0,0},{11,10,10,14},{0,0,0,0},{0,0,0,0}}, + {{0,7,0,0},{0,5,0,0},{0,5,0,0},{0,13,0,0}}, + {{0,0,0,0},{11,10,10,14},{0,0,0,0},{0,0,0,0}}, + {{0,7,0,0},{0,5,0,0},{0,5,0,0},{0,13,0,0}}}, + {{{0,0,0,0},{11,2,14,0},{0,13,0,0},{0,0,0,0}}, + {{0,7,0,0},{11,4,0,0},{0,13,0,0},{0,0,0,0}}, + {{0,7,0,0},{11,8,14,0},{0,0,0,0},{0,0,0,0}}, + {{0,7,0,0},{0,1,14,0},{0,13,0,0},{0,0,0,0}}}}; diff --git a/source/canvas.cpp b/source/canvas.cpp new file mode 100644 index 0000000..70955fa --- /dev/null +++ b/source/canvas.cpp @@ -0,0 +1,1058 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "music.h" +#include "input.h" +#include "random.h" +#include "bloc.h" +#include "quadra.h" +#include "config.h" +#include "res.h" +#include "raw.h" +#include "pcx.h" +#include "zone.h" +#include "game.h" +#include "global.h" +#include "sons.h" +#include "recording.h" +#include "texte.h" +#include "chat_text.h" +#include "nglog.h" +#include "canvas.h" + +Canvas::Canvas(int qplayer, int game_seed, Palette *p): rnd(game_seed) { +// construit un Canvas local + snapshot[0]=0; + best_move=best_clean=best_recurse=0; + memset(player_hash, 0, sizeof(player_hash)); + memset(team_hash, 0, sizeof(team_hash)); + collide_side_only=false; + should_remove_bonus=false; + wait_download=false; + z_lines=z_potatolines=z_linestot=z_potatolinestot=NULL; + team_potato_lines=team_potato_linestot=0; + send_for_clean=false; + potato_team_on_last_stamp=255; + potato_lines=0; + gone_time=0; + inter=NULL; + h_repeat = v_repeat = 0; + smooth = shadow = false; + moves=NULL; + pal = p; + bit=NULL; //Somebody somewhere will set that. Sucks. + player = qplayer; + if(playback) { + color = playback->player[player].color; + strcpy(name, playback->player[player].name); + continuous=1; + } else { + handicap = config.player2[player].handicap; + color = config.player[player].color; + strcpy(name, config.player[player].name); + strcpy(team_name, config.player2[player].ngTeam); + continuous=config.player2[player].continuous; + } + remote_adr=NULL; + local_player = true; + hide(); + init(); +} + +Canvas::Canvas(int game_seed, Byte team, const char *nam, int ph_repeat, int pv_repeat, bool psmooth, bool pshadow, int phandicap, Net_connection *adr, int qplayer, bool wait_down): rnd(game_seed) { +// construit un Canvas remote + snapshot[0]=0; + best_move=best_clean=best_recurse=0; + memset(player_hash, 0, sizeof(player_hash)); + memset(team_hash, 0, sizeof(team_hash)); + collide_side_only=false; + should_remove_bonus=false; + wait_download=wait_down; + z_lines=z_potatolines=z_linestot=z_potatolinestot=NULL; + team_potato_lines=team_potato_linestot=0; + send_for_clean=false; + potato_team_on_last_stamp=255; + potato_lines=0; + gone_time=0; + inter=NULL; + handicap = phandicap; + h_repeat = ph_repeat; + v_repeat = pv_repeat; + continuous=1; //Doesn't matter for remote canvas + smooth = psmooth; + shadow = pshadow; + moves=NULL; + pal = NULL; + bit = NULL; + player = qplayer; + color = team; + strncpy(name, nam, sizeof(name)); + name[sizeof(name)-1]=0; + remote_adr=adr; + local_player = false; + hide(); + init(); +} + +Canvas::~Canvas() { + delete over; + delete myself; + if(moves) + delete moves; + delete_bloc(); + if(sprlevel_up) + delete sprlevel_up; + watchers.deleteall(); +} + +char *Canvas::long_name(bool handi, bool gone) { + static char ret[64]; + strcpy(ret, name); + if(handi) { + char *h=""; + switch(handicap) { + case 0: h=" (-)"; break; + case 1: h=" (A)"; break; + case 3: h=" (M)"; break; + case 4: h=" (+)"; break; + } + strcat(ret, h); + } + if(gone) + if(idle==3) + strcat(ret, " *"); + return ret; +} + +void Canvas::delete_bloc() { + if(bloc) + delete bloc; + if(next) + delete next; + if(next2) + delete next2; + if(next3) + delete next3; + if(bloc_shadow) + delete bloc_shadow; + bloc = next = next2 = next3 = bloc_shadow = NULL; +} + +void Canvas::init() { + trying_to_drop=false; + { + Res_doze res("GAMELVUP.RAW"); + Raw raw(res); + Bitmap bitmap(raw); + sprlevel_up = new Sprite(bitmap, 0, 0); + } + over = new Overmind(); + bloc = next = next2 = next3 = bloc_shadow = NULL; + reinit(); + myself = new Executor(); + myself->add(new Player_init(this)); + over->start(myself); +} + +void Canvas::reinit() { + set_message("", ""); + int i,j; + watch_date = 0; + for(j=0; j<36; j++) + for(i=0; i<18; i++) { + occupied[j][i] = false; + block[j][i] = 0; + blinded[j][i] = 0; + bflash[j][i] = 0; + } + for(i=0; i<20; i++) + flash[i] = 0; + if(islocal()) { + if(playback) { + //playback->multi_mode is certainly false since this canvas + // is local. Thus, we don't care about handicap + h_repeat = v_repeat = playback->player[player].repeat; + continuous = 1; + smooth = playback->player[player].smooth ? true:false; + shadow = playback->player[player].shadow ? true:false; + } else { + handicap = config.player2[player].handicap; + h_repeat = config.player2[player].h_repeat; + v_repeat = config.player2[player].v_repeat; + continuous = config.player2[player].continuous; + smooth = config.player[player].smooth ? true:false; + shadow = config.player[player].shadow ? true:false; + } + } + if(game->normal_attack.type==ATTACK_BLIND || game->normal_attack.type==ATTACK_FULLBLIND) + shadow=true; + if(game->potato_normal_attack.type==ATTACK_BLIND || game->potato_normal_attack.type==ATTACK_FULLBLIND) + shadow=true; + switch(h_repeat) { + case 0: + h_repeat_delay = 11; + break; + case 1: + h_repeat_delay = 6; + break; + case 2: + h_repeat_delay = 3; + break; + case 3: + h_repeat_delay = 1; + break; + } + side_speed = (18<<4)/h_repeat_delay; + switch(v_repeat) { + case 0: + v_repeat_delay = 11; + break; + case 1: + v_repeat_delay = 6; + break; + case 2: + v_repeat_delay = 3; + break; + case 3: + v_repeat_delay = 1; + break; + } + down_speed = (340)/v_repeat_delay; + if(down_speed > 180) + down_speed = 180; + level = game->level_start; + lines = depth = complexity = bonus = 0; + level_up = color_flash = 0; + over->framecount = 0; // initialise le compteur lorsque le joueur demarre + if(game->net_version()>=23 && game->survivor) + idle = 2; + else + idle = 1; + dying = false; + state = PLAYING; + potato_team_on_last_stamp=255; + send_for_clean=false; + should_remove_bonus=false; +} + +void Canvas::restart() { + if(islocal()) + clear_key_all(); + init_block(); + clear_tmp(); + delete_bloc(); + set_next(); + last_attacker = 255; + for(int i=0; ilevel_start; + lines = depth = complexity = bonus = 0; + level_up = color_flash = 0; + calc_speed(); + idle = 1; // start 'idle' pour permettre de joindre si game sur pause et joueur tout juste demarre + state = PLAYING; + dying=false; + potato_team_on_last_stamp=255; + send_for_clean=false; + should_remove_bonus=false; + set_message("", ""); +} + +void Canvas::clear_key_all() { + for(int i=0; i<7; i++) // vide le statut des touches du joueur + clear_key(i); +} + +void Canvas::calc_shadow() { + if(!bloc_shadow) + bloc_shadow = new Bloc(bloc->quel, 8, 0, 0); + bloc_shadow->rot = bloc->rot; + bloc_shadow->bx = bloc->bx; + bloc_shadow->by = bloc->by; + while(!check_collide(bloc_shadow, bloc_shadow->bx, bloc_shadow->by+1, bloc_shadow->rot)) + bloc_shadow->by++; + bloc_shadow->calc_xy(); + //bloc_shadow->x = bloc->x; +} + +void Canvas::init_block() { + int x, y; + for(y=32; y < 36; y++) + for(x = 0; x < 18; x++) + occupied[y][x] = true; + for(y = 0; y < 32; y++) { + for(x = 0; x < 4; x++) { + occupied[y][x] = true; + occupied[y][x+14] = true; + } + for(x = 4; x < 14; x++) { + block[y][x] = 0; + occupied[y][x] = false; + } + } + for(x = 0; x < 18; x++) + tmp[32][x] = 1; + for(y = 0; y < 32; y++) { + for(x = 0; x < 4; x++) + tmp[y][x] = 1; + for(x = 14; x < 18; x++) + tmp[y][x] = 1; + } + for(y = 0; y < 36; y++) + for(x = 0; x < 14; x++) + dirted[y][x]=2; + for(y = 0; y < 36; y++) + for(x = 0; x < 18; x++) { + blinded[y][x] = 0; + bflash[y][x] = 0; + } +} + +void Canvas::draw_block(int j, int i) const { + Byte side, col, to[4]; + side = block[j][i]&15; + col = block[j][i]>>4; + to[0] = block[j][i-1]; + to[1] = block[j-1][i]; + to[2] = block[j][i+1]; + to[3] = block[j+1][i]; + raw_draw_bloc_corner(screen, (i-4)*18, (j-12)*18, side, ::color[col],to); +} + +void Canvas::calc_speed() { + //speed = level * 4; + if(level<=10) + speed = 4 + (level-1)*5; + else + speed = 50 + (level-10)*3; +} + +void Canvas::set_next() { + if(znext) { + znext->set_next(next); + znext2->set_next(next2); + znext3->set_next(next3); + } +} + +void Canvas::set_message(const char *m1, const char *m2) { + strncpy(msg1, m1, sizeof(msg1)); + msg1[sizeof(msg1)-1]=0; + strncpy(msg2, m2, sizeof(msg2)); + msg2[sizeof(msg2)-1]=0; +} + +void Canvas::add_text_scroller(const char *st, int xoffset, int yoffset) { + if(inter && !small_watch) { // si canvas visible presentemment + Executor *tmp2 = new Executor(true); + tmp2->add(new Player_text_scroll(this, st, xoffset, yoffset)); + over->start(tmp2); + } +} + +void Canvas::blind_all(Byte time) { + if(idle<2 && !dying) { + int x, y; + for(y = 0; y < 36; y++) + for(x = 0; x < 18; x++) { + int tmp = blinded[y][x]; + if(occupied[y][x]) { + if(!tmp && time) { + bflash[y][x]=32; + dirted[y][x]=2; + } + tmp=min(255, tmp+time); + blinded[y][x] = tmp; + } + } + } +} + +void Canvas::add_packet(Canvas *sender, Byte nb, Byte nc, Byte lx, Attack attack, Word hole_pos[]) { + int x, qui; + if(!sender) + return; + + log_step("player_attacked\t%u\t%u\t%s\t%i", id(), sender->id(), attack.log_type(), attack.type==ATTACK_FULLBLIND? nb*nc:nb); + + //Nothing further to do if attack is none. + if(attack.type==ATTACK_NONE) + return; + + //Update last_attacker and attacks array + qui = game->net_list.canvas2player(sender); + int temp = attacks[qui] + nb*2; + if(temp > 255) + temp = 255; + attacks[qui] = temp; + if(last_attacker != 255) { // si ya un dernier attackant + if(attacks[qui] >= attacks[last_attacker]) // si plus gros ou egal au dernier plus hot + last_attacker = qui; // c'est lui qui devient le plus hot + } else + last_attacker = qui; + + //If full-blind attack, blind canvas and return + if(attack.type==ATTACK_FULLBLIND) { + blind_all(nb*nc*attack.param); + return; + } + + //Attack type is either blind or lines, add bonuses + if(bonus < 20) { + if(nb+bonus > 20) + nb = 20-bonus; + nc--; + int normal = max(nb - nc, 0); + int fucked = nb - normal; + if(game->net_version()>=23) { + for(x=0; xcolor; + bon[x+bonus].blind_time = attack.type==ATTACK_BLIND? attack.param:0; + bon[x+bonus].hole_pos=hole_pos[x]; + if(x==nb-1) + bon[x+bonus].final=true; + else + bon[x+bonus].final=false; + } + bonus += nb; + } + + if(game->net_version()<23) { + for(x=0; xcolor; + bon[x+bonus].blind_time = attack.type==ATTACK_BLIND? attack.param:0; + } + bonus += normal; + + for(x=0; x 13) + lx -= 10; + bon[x+bonus].x = lx; + bon[x+bonus].color = sender->color; + bon[x+bonus].blind_time = attack.type==ATTACK_BLIND? attack.param:0; + } + bonus += fucked; + } + } +} + +void Canvas::give_line() { + if(!depth) + return; + int i, score_add; + int clean_bonus=0; + bool log_it=false; + Word move_value=(depth<<8)+complexity; + if(send_for_clean) { + clean_bonus=(1+depth)/2; + if(move_value>best_clean) { + log_it=true; + best_clean=move_value; + } + } + if(move_value>best_move) { + log_it=true; + best_move=move_value; + } + move_value=(complexity<<8)+depth; + if(move_value>best_recurse) { + log_it=true; + best_recurse=move_value; + } + if(log_it) + log_step("player_snapshot\t%i\t%i\t%s\t%i\t%s", id(), depth, send_for_clean? "true":"false", complexity, snapshot); + switch(depth) { + case 1: score_add = 250; break; + case 2: score_add = 500; break; + case 3: score_add = 1000; break; + case 4: score_add = 2000; break; + default: score_add = 200 * depth * depth; break; + } + int complexity_points=1000*(complexity-1); + if(game->net_version()>=23) + complexity_points=200*(complexity-1)*(complexity-1); + score_add += complexity_points; + //0 clean_points for net_version<23 cause it was added in check_clean + if(send_for_clean && game->net_version()>=23) { + int clean_points; + if(depth<=4) + clean_points=depth*1250; + else + clean_points=depth*depth*500; + score_add += clean_points; + } + score_add += (score_add/10)*level; + stats[SCORE].add(score_add); + i = depth-1; + bool enough=(depth >= game->combo_min); + if(game->net_version()>=23) { + int alive_count=0; + for(i=0; inet_list.get(i); + if(c && c->idle<2) + alive_count++; + } + //alive_count adjustment only if there's at least 5 alive + if(alive_count>4) + alive_count-=4; + else + alive_count=0; + i = max(0, depth-1-alive_count); + enough=i? true:false; + } + log_step("player_lines_cleared\t%u\t%i\t%s\t%i\t%i\t%u", id(), depth, send_for_clean? "true":"false", clean_bonus+(enough? i:0), complexity, score_add); + Attack clean_att, normal_att; + normal_att=game->normal_attack; + clean_att=game->clean_attack; + if(game->hot_potato && color==game->potato_team) { + normal_att=game->potato_normal_attack; + clean_att=game->potato_clean_attack; + } + if(color==game->potato_team && enough) + potato_lines += (i+clean_bonus); + if(send_for_clean) { + game->net_list.send(this, clean_bonus, complexity, last_x, clean_att, true); + if(chat_text) { + char st[256]; + int num; + if(normal_att.type==ATTACK_NONE) { + num=depth; + if(num>1) + sprintf(st, ST_CLEANBOBCLEARSBOBLINES, name, num); + else + sprintf(st, ST_CLEANBOBCLEARS1LINE, name); + } + else { + num=clean_bonus; + if(enough) + num+=i; + if(num>1) + sprintf(st, ST_BOBCLEANBOBLINES, name, num); + else + sprintf(st, ST_BOBCLEAN1LINE, name); + } + message(color, st); + } + } + if(i && enough) { // envoie rien si depth < que combo_min + if(i>=3 && chat_text && !send_for_clean) { + // si fait un 'quad' minimum et pas clean + char st[256]; + if(normal_att.type==ATTACK_NONE) + sprintf(st, ST_BOBCLEARSBOBLINEBOB, name, depth, depth!=1? "s":""); + else + sprintf(st, ST_SENDLINES, name, i, i!=1? "s":""); + message(color, st); + } + game->net_list.send(this, i, complexity, last_x, normal_att, false); + if(inter && !small_watch) { // si canvas visible presentemment + char st[256]; + if(depth == 2) + sprintf(st, ST_CLEARDOUBLE, score_add); + if(depth == 3) + sprintf(st, ST_CLEARTRIPLE, score_add); + if(depth == 4) + sprintf(st, ST_CLEARQUAD, score_add); + if(depth > 4) + sprintf(st, ST_CLEARMORE, depth, score_add); + add_text_scroller(st, 20); + } + } + i=depth-1; //For stats accounting, use old numbers + if(i >= 14) { + stats[CLEAR14+i-14].add(1); + i = 14; //Add in CLEARMORE + } + stats[CLEAR00+i].add(1); + stats[COMBO00+complexity-1].add(1); + + lines += depth; + stats[LINESTOT].add(depth); + if(game->level_up && lines >= level*15) { + level++; + calc_speed(); + Executor *tmp = new Executor(true); + tmp->add(new Player_level_up(this)); + over->start(tmp); + } + depth = 0; + complexity=0; + send_for_clean=false; +} + +void Canvas::change_level_single() { + change_level(level, pal, bit); + //video->setpal(*pal); + if(config.info.cdmusic == 1) + music->play(level+1); + if(level <= 10 && (level-1) > config.info.unlock_theme && !playback) { + config.info.unlock_theme = level-1; + config.write(); + } +} + +void Canvas::change_level(const int level, Palette *pal, Bitmap *bit) { + int num, i; + num = (level-1)%10; +// if(level>5) +// num=config.info.multi_level-1; + sprintf(st, "Fond%i.pcx", num); + if(level==-1) + strcpy(st, "black.pcx"); + Res_doze *res = new Res_doze(st); + Pcx img(*res); + bit->reload(img); + Palette pal2(img); + for(i=0; i<256; i++) // was 184 + pal->setcolor(i, pal2.r(i), pal2.g(i), pal2.b(i)); + + for(i=0; icolorize(*pal, pal2.r(i*8+184), pal2.g(i*8+184), pal2.b(i*8+184)); + fteam[MAXTEAMS-1]->colorize(*pal, 170, 170, 170); + + video->need_paint = 2; + delete res; + if(sons.flash) + delete sons.flash; + if(sons.depose3) + delete sons.depose3; + if(sons.depose2) + delete sons.depose2; + if(sons.depose) + delete sons.depose; + if(sons.drip) + delete sons.drip; + sons.flash = sons.depose3 = sons.depose2 = sons.depose = sons.drip = NULL; + char *foo0, *foo1, *foo2, *foo3, *foo4; + switch(num) { + case 1: + foo0="Pwap2.wav"; + foo1=foo2=foo3="Knock.wav"; + foo4="click_3.wav"; + break; + case 2: + foo0="Blip1.wav"; + foo1="metal3.wav"; + foo2="Metal1.wav"; + foo3="Metal6.wav"; + foo4="click_1.wav"; + break; + case 3: + foo0="Whistle1.wav"; + foo1="Tapdrip.wav"; + foo2="Click01.wav"; + foo3="click_3.wav"; + foo4="Click01.wav"; + break; + case 4: + foo0="Spring.wav"; + foo1="Pop1.wav"; + foo2="bloop.wav"; + foo3="Pwap2.wav"; + foo4="corkpop.wav"; + break; + case 5: + foo0="Whistle2.wav"; + foo1="Knock.wav"; + foo2="Splodge.wav"; + foo3="Pop1.wav"; + foo4="Tapdrip.wav"; + break; + case 6: + foo0="Glass04.wav"; + foo1="Glass01.wav"; + foo2="Glass03.wav"; + foo3="Glass03.wav"; + foo4="Click01.wav"; + break; + case 7: + foo0="Bubble2.wav"; + foo1="Water05_1.wav"; + foo2="water05_2.wav"; + foo3="water05_3.wav"; + foo4="Click01.wav"; + break; + case 8: + foo0="Ceramic3.wav"; + foo1="Explod03.wav"; + foo2="Explod05.wav"; + foo3="Explod06.wav"; + foo4="Tapdrip.wav"; + break; + case 9: + foo0="Smash2.wav"; + foo1="Knock.wav"; + foo2="bloop.wav"; + foo3="click_1.wav"; + foo4="Pop1.wav"; + break; + default: + foo0="Pwap2.wav"; + foo1="Hitwood1.wav"; + foo2="Chop2.wav"; + foo3="metal3.wav"; + foo4="Tapdrip.wav"; + break; + } + { + Res_doze res(foo0); + sons.flash = new Sample(res, 2); // quand fait une ligne (flash) + } + { + Res_doze res(foo1); + sons.depose3 = new Sample(res, 2); // depose + } + { + Res_doze res(foo2); + sons.depose2 = new Sample(res, 2); // depose + } + { + Res_doze res(foo3); + sons.depose = new Sample(res, 2); // depose + } + { + Res_doze res(foo4); + sons.drip = new Sample(res, 2); // rotate + } +} + +void Canvas::clear_tmp() { + int i, j; + for(j = 0; j < 32; j++) + for(i = 4; i < 14; i++) + tmp[j][i] = 0; + for(j=0; j<36; j++) + for(i=0; i<18; i++) + moved[j][i]=false; +} + +void Canvas::set_canvas_pos(int px, int py, Bitmap *fo, Video_bitmap *s, Zone_next *z, Zone_next *z2, Zone_next *z3, Inter *in) { + x = px; + y = py; + fond = fo; + screen = s; + znext = z; + znext2 = z2; + znext3 = z3; + set_next(); + inter = in; + small_watch = false; +} + +void Canvas::hide() { + x = 0; + y = 0; + fond = NULL; + screen = NULL; + znext = NULL; + znext2 = NULL; + znext3 = NULL; + inter = NULL; + z_lines=z_potatolines=z_linestot=z_potatolinestot=NULL; +} + +Byte Canvas::check_key(int i) { + if(ecran && ecran->focus) { // empeche de controler pendant de l'input dans un zone_text_input qui a le focus + clear_key(i); // empeche le 'rotate' de s'effectuer apres un input (car bit 'was released!') + return 0; + } + else + if(i<5) + return input->keys[config.player[player].key[i]]; + else + return input->keys[config.player2[player].key[i-5]]; +} + +void Canvas::clear_key(int i) { + if(i<5) + input->keys[config.player[player].key[i]] = 0; + else + input->keys[config.player2[player].key[i-5]] = 0; +} + +void Canvas::unrelease_key(int i) { + if(i<5) + input->keys[config.player[player].key[i]] &= ~RELEASED; + else + input->keys[config.player2[player].key[i-5]] &= ~RELEASED; +} + +void Canvas::blit_level_up() { + sprlevel_up->draw(screen, 10, level_up-30); + dirt_rect(10, level_up-30, sprlevel_up->width, sprlevel_up->height); +} + +void Canvas::blit_flash() { + int i, j, pj; + for(j = 0; j < 20; j++) { + pj = flash[j]; + if(pj) { + for(i=0; i<18; i++) + screen->hline((pj-12)*18+i, 0, 10*18-1, color_flash); + dirt_rect(0, (pj-12)*18, 10*18, 18); + } + } +} + +void Canvas::dirt_rect(int x1, int y1, int w1, int h1) { + int i,j; + w1 = (x1+w1+17)/18; + h1 = (y1+h1+17)/18; + x1 = x1/18; + y1 = y1/18; + x1 = max(0,x1); + y1 = max(0,y1); + w1 = min(10,w1); + h1 = min(20,h1); + for(j=y1; jbloc[bloc->quel][rot][j][i]) + if(occupied[py + j][px + i]) { + if(px+i>=4 && px+i<14) + collide_side_only=false; + ret=true; + } + } + if(ret) { + return ret; + } + else { + collide_side_only=false; + return false; + } +} + +void Canvas::step_bflash() { + int j, i; + for(j = 12; j < 32; j++) + for(i = 4; i < 14; i++) { + if(occupied[j][i] && bflash[j][i]) { + if(!(bflash[j][i]&3)) + dirted[j][i] = 2; + bflash[j][i]--; + if(!bflash[j][i]) + dirted[j][i] = 2; + } + } +} + +void Canvas::blit_back() { + step_bflash(); + int j, i, x2, y2; + for(j = 12; j < 32; j++) + for(i = 4; i < 14; i++) { + if(dirted[j][i]) { + bool blitbloc=true; + if(!occupied[j][i]) + blitbloc=false; + if(bflash[j][i]) + if(bflash[j][i]&4) + blitbloc=false; + if(!bflash[j][i] && blinded[j][i]) + blitbloc=false; + if(blitbloc) { + draw_block(j,i); + } else { + x2=(i-4)*18; + y2=(j-12)*18; + Bitmap tmp((*fond)[y2]+x2, 18, 18, fond->realwidth); + tmp.draw(screen, x2, y2); + } + dirted[j][i]--; + } + } + if(msg1[0] && inter) + inter->font->draw(msg1, video->vb, x+5, y+40); + if(msg2[0] && inter) + inter->font->draw(msg2, video->vb, x+5, y+60); +} + +void Canvas::blit_bloc(Bloc *blo) { + int j,i,bx,by,tx,ty; + if(!blo) + return; + if(smooth) { + blo->draw(screen); + } else { + blo->draw(screen, (blo->bx-4)*18, (blo->by-12)*18); + } + for(j=0; j<4; j++) + for(i=0; i<4; i++) { + if(blo->bloc[blo->quel][blo->rot][j][i]) { + if(smooth) { + tx=(blo->x>>4)+4*18; + ty=(blo->y>>4)+12*18; + } else { + tx=blo->bx*18; + ty=blo->by*18; + } + tx += i*18; + ty += j*18; + bx=tx/18; + by=ty/18; + if(by>=0) + dirted[by][bx]=2; + bx=(tx+17)/18; + if(by>=0) + dirted[by][bx]=2; + by=(ty+17)/18; + if(by>=0) + dirted[by][bx]=2; + bx=tx/18; + if(by>=0) + dirted[by][bx]=2; + } + } +} + +void Canvas::small_draw_block(int j, int i) const { + Byte side, col; + side = block[j][i]&15; + col = block[j][i]>>4; + raw_small_draw_bloc(screen, (i-4)*6, (j-12)*6, side, ::color[col]); +} + +void Canvas::small_blit_back() { + step_bflash(); + int j, i, x2, y2; + for(j = 12; j < 32; j++) + for(i = 4; i < 14; i++) + if(dirted[j][i]) { + bool blitbloc=true; + if(!occupied[j][i]) + blitbloc=false; + if(bflash[j][i]) + if(bflash[j][i]&4) + blitbloc=false; + if(!bflash[j][i] && blinded[j][i]) + blitbloc=false; + if(blitbloc) { + small_draw_block(j,i); + } else { + x2=(i-4)*6; + y2=(j-12)*6; + Bitmap tmp((*fond)[y2]+x2, 6, 6, fond->realwidth); + tmp.draw(screen, x2, y2); + } + dirted[j][i]--; + } +} + +void Canvas::small_blit_bloc(Bloc *blo) { + int j,i,bx,by,tx,ty; + if(!blo) + return; + blo->small_draw(screen, (blo->bx-4)*6, (blo->by-12)*6); + for(j=0; j<4; j++) + for(i=0; i<4; i++) { + if(blo->bloc[blo->quel][blo->rot][j][i]) { + tx=blo->bx*6; + ty=blo->by*6; + tx += i*6; + ty += j*6; + bx=tx/6; + by=ty/6; + if(by>=0) + dirted[by][bx]=2; + bx=(tx+5)/6; + if(by>=0) + dirted[by][bx]=2; + by=(ty+5)/6; + if(by>=0) + dirted[by][bx]=2; + bx=tx/6; + if(by>=0) + dirted[by][bx]=2; + } + } +} + +void Canvas::small_blit_flash() { + int i, j, pj; + for(j = 0; j < 20; j++) { + pj = flash[j]; + if(pj) { + for(i=0; i<6; i++) + screen->hline((pj-12)*6+i, 0, 10*6-1, color_flash); + dirt_rect(0, (pj-12)*18, 10*18, 18); + } + } +} + +void Canvas::add_watcher(Watcher *w) { + watchers.add(w); +} + +void Canvas::remove_watcher(Net_connection *nc) { + for(int i=0; inc == nc) { + delete watchers[i]; + watchers.remove(i); + i--; + } +} + +bool Canvas::islocal() const { + return local_player; +} + +void Canvas::start_moves() { + if(game->wants_moves) { + if(moves) + delete moves; + moves=new Packet_clientmoves(); + moves->player = num_player; + } +} + +void Canvas::send_p_moves() { + if(game->wants_moves) { + if(moves) { + net->sendtcp(moves); + delete moves; + moves=NULL; + } + } +} + +void Canvas::start_byte() { + if(game->wants_moves) { + if(!moves) + start_moves(); + if(moves->size>=50) { + send_p_moves(); + start_moves(); + } + moves->start_byte(); + } +} + +void Canvas::set_bit(int v) { + if(moves) + moves->set_bit(v); +} + +void Canvas::write_byte() { + if(moves) + moves->write_byte(); +} diff --git a/source/chat_text.cpp b/source/chat_text.cpp new file mode 100644 index 0000000..8721c13 --- /dev/null +++ b/source/chat_text.cpp @@ -0,0 +1,136 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "config.h" +#include "net_stuff.h" +#include "sons.h" +#include "canvas.h" +#include "game.h" +#include "nglog.h" +#include "chat_text.h" + +Chat_text *chat_text=NULL; +int Chat_text::quel_player=0; + +Chat_text::Chat_text(Fontdata *f, int wid) { + font = f; + clear(); + new_text = false; + w = wid; +} + +void Chat_text::add_text(int team, const char *text, bool sound) { + char tmp[256]; + strncpy(tmp, text, 256); + tmp[255]=0; + int color_cut=-1; + if(team&16 && team>0) { + team-=16; + char *col=strchr(tmp, ':'); + if(col) + color_cut=col-tmp; + } + int wid, last_s, i; + char *tm = tmp; + do { + scroll_up(); + list[CHAT_NBLINE-1].team = team; + if(color_cut!=-1) + team=-1; + list[CHAT_NBLINE-1].color_cut = color_cut; + color_cut=-1; + last_s = -1; + for(i=0; (unsigned int)i < strlen(tm); i++) { + wid = font->width(tm, i+1); + if(wid > w-2) { + int skip_space=1; + if((last_s != -1 && font->width(tm, last_s) < w/2) || (last_s == -1)) { + last_s = i-1; + skip_space=0; + } + strncpy(list[CHAT_NBLINE-1].text, tm, last_s); + list[CHAT_NBLINE-1].text[last_s] = 0; + tm += last_s+skip_space; + i = -1; + break; + } else + if(tm[i] == 32) + last_s = i; + } + if(i != -1) { + strcpy(list[CHAT_NBLINE-1].text, tm); + } + } while(i == -1); + new_text = true; + + if(game && !game->single && team != -1 && sound) + Sfx stmp(sons.fadeout, 0, -200, 0, 28000); +} + +void Chat_text::scroll_up() { + for(int i=1; iaddwatch(P_CHAT, this); +} + +void Chat_text::removewatch() { + net->removewatch(P_CHAT, this); +} + +void Chat_text::net_call(Packet *p2) { + Packet_chat *p=(Packet_chat *)p2; + static Dword last_sound=0; + bool ok = false; + if(p->to_team != -1 && game) { + for(int i=0; inet_list.get(i); + if(c && c->color == p->to_team && c->islocal()) { + ok = true; + break; + } + } + } else { + ok = true; + } + if(ok || (game && game->server)) { + if(last_sound-overmind.framecount>=4) { + last_sound=overmind.framecount; + Sfx stmp(sons.msg, 0, 0, 0, 11025); + } + message(p->team|16, p->text, false, true, !ok); + } + delete p2; +} + +void Chat_text::clear() { + to_player=0; + for(int i=0; iadd_text(color, text, sound); + if(!game || !game->server) + return; + int co; + for(co=0; coconnections.size(); co++) { + Net_connection *nc=net->connections[co]; + if(nc && (nc->trusted || !trusted) && nc!=but && nc!=game->loopback_connection) + if(!in_packet || !nc->packet_based) + send_msg(nc, "%s", text); + } +} diff --git a/source/color.cpp b/source/color.cpp new file mode 100644 index 0000000..9f934df --- /dev/null +++ b/source/color.cpp @@ -0,0 +1,26 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "color.h" + +Color::Color(Byte q, Palette& p): pal(p) { + if(q==8) + q=7; + else + if(q==7) + q=8; + base = q*8 + 184; +} + +void Color::set(int r, int g, int b, int r2, int g2, int b2) { + int i; + for(i=0; i<5; i++) { + pal.setcolor(i+base, (r*i)/4,(g*i)/4,(b*i)/4); + } + for(i=5; i<8; i++) { + pal.setcolor(i+base, (r*(7-i)+r2*(i-4))/3, (g*(7-i)+g2*(i-4))/3, (b*(7-i)+b2*(i-4))/3); + } +} diff --git a/source/config.cpp b/source/config.cpp new file mode 100644 index 0000000..6323864 --- /dev/null +++ b/source/config.cpp @@ -0,0 +1,223 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif +#include +#include +#include "input.h" +#include "res.h" +#include "global.h" +#include "registry.h" +#include "crypt.h" +#include "video.h" +#include "config.h" + +const int Config::game_version = 19; +int Config::net_version = 23; + +const int Config::major = 1; +const int Config::minor = 1; +const int Config::patchlevel = 4; +bool Config::registered = false; +bool Config::xtreme = false; +char Config::user_name[64] = {""}; + +Config::Config() { + fname[0]=0; +} + +Config::~Config() { +} + +void Config::default_config() { + memset(&version, 0, sizeof(version)); + memset(&info, 0, sizeof(info)); + memset(player, 0, sizeof(player)); + memset(player2, 0, sizeof(player2)); + memset(&info2, 0, sizeof(info2)); + info.language = 0; + info.setup_player = 0; + info.cdmusic = 1; // 0=no music 1=auto-change 2=loop all + info.multi_level = 1; + info.unlock_theme = 0; + info.port_number = 3456; + info.mouse_speed = 100; + info.pane[0] = 2; + info.pane[1] = 0; + info.pane[2] = 3; + info.update_rate = 10; + memset(info.book, sizeof(info.book), 0); + info.game_name[0] = 0; + info.game_type = info.level_up = info.level_start = info.combo_min = info.game_end = 0; + info.game_public = 1; + info.game_end_value = 1; + strcpy(info.game_server_address, ""); + + for(int i=0; i<3; i++) { + sprintf(st,"#%i", i+1); + strcpy(player[i].name, st); + player[i].color = i; + player[i].shadow = 0; + player[i].smooth = 1; + player[i].repeat = -1; + player2[i].h_repeat = 2; + player2[i].v_repeat = 2; + player2[i].continuous = 1; + player[i].key[0] = KEY_LEFTARROW; + player[i].key[1] = KEY_RIGHTARROW; + player[i].key[2] = KEY_UPARROW; + player[i].key[3] = KEY_DOWNARROW; + player[i].key[4] = KEY_UPARROW; + player2[i].key[0] = KEY_RSHIFT; + player2[i].key[1] = KEY_SPACE; + } +} + +void Config::read() { + if(!fname[0]) + sprintf(fname, "%s/%s", quadradir, "quadra.cfg"); + + int i; + + Res_dos res(fname, RES_TRY); + memset(&version, 0, sizeof(version)); + memset(&info, 0, sizeof(info)); + memset(player, 0, sizeof(player)); + memset(player2, 0, sizeof(player2)); + memset(&info2, 0, sizeof(info2)); + warning = 0; + if(!res.exist) { + default_config(); + warning = 1; + } else { + if(res.size() != (sizeof(version) + sizeof(info) + sizeof(player)) && + res.size() != (sizeof(version) + sizeof(info) + sizeof(player) + sizeof(player2) + sizeof(info2))) { + default_config(); + warning = 2; + } else { + version = -1; + res.read(&version, sizeof(version)); + if(version != game_version) { + default_config(); + warning = 2; + } else { + res.read(&info, sizeof(info)); + res.read(player, sizeof(player)); + //Those may not be present, but the default is all-zero anyway + res.read(player2, sizeof(player2)); + res.read(&info2, sizeof(info2)); + } + } + } + + for(i=0; i<3; i++) { + player[i].name[39] = 0; + if(player[i].color<0 || player[i].color>=MAXTEAMS) + player[i].color=i; + if(player[i].shadow<0 || player[i].shadow>1) + player[i].shadow=1; + if(player[i].smooth<0 || player[i].smooth>1) + player[i].smooth=1; + if(player[i].repeat<-1 || player[i].repeat>3) + player2[i].h_repeat=player2[i].v_repeat=2; + else + if(player[i].repeat>=0) { + player2[i].h_repeat=player[i].repeat; + player2[i].v_repeat=player[i].repeat; + } + if(player2[i].h_repeat<0 || player2[i].h_repeat>3) { + player2[i].h_repeat=2; + } + if(player2[i].v_repeat<0 || player2[i].v_repeat>3) { + player2[i].v_repeat=2; + } + player[i].repeat=-1; + if(player2[i].continuous<0 || player2[i].continuous>1) + player2[i].continuous=1; + if(player2[i].handicap<0 || player2[i].handicap>4) + player2[i].handicap=0; + player2[i].ngPasswd[63]=0; + player2[i].ngTeam[39]=0; + player2[i].ngTeamPasswd[63]=0; + } + for(i=0; i<10; i++) { + info.book[i][255] = 0; + } +} + +void Config::check_register() { + Registry *r = Registry::alloc(); + r->open("Quadra", quadradir); + #ifdef XTREME_GAMES + r->write("User name", "Xtreme Games LLC"); + r->write("Password", "e5198e093d78ee3f726e33c3c032d8d5"); + #endif + char name_buf[1024]; + name_buf[0] = 0; + r->read("User name", name_buf, 1024); + char pass_buf[1024]; + pass_buf[0] = 0; + r->read("Password", pass_buf, 1024); + char st[1024]; + sprintf(st, "%i.%i.%i", major, minor, patchlevel); + r->write("Version", st); + r->close(); + delete r; + if(strlen(name_buf) > 0 && strlen(pass_buf) > 0) { + Crypt cr(name_buf, true); + if(stricmp(cr.get_digest_string(), pass_buf) == 0) { + registered = true; + strncpy(user_name, name_buf, 63); + user_name[63] = 0; + if(!strcmp(user_name, "Xtreme Games LLC")) + xtreme = true; + } + } +} + +void fix_str(char *st, Dword len) { + bool in_str=true; + Dword i; + for(i=0; i $@.sym + strip $@ +endif + +source/quadra.res: $(QUADRA_RES) + $(SKELTON)/tools/wadder/wadder $@ resources.txt + +endif diff --git a/source/fonts.cpp b/source/fonts.cpp new file mode 100644 index 0000000..70f6be9 --- /dev/null +++ b/source/fonts.cpp @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "res.h" +#include "sprite.h" +#include "fonts.h" + +void Fonts::init() { + { + Res_doze res("font.fnt"); + normal = new Fontdata(res, 2); + } + { + Res_doze res("courrier.fnt"); + courrier = new Fontdata(res, 1); + } +} + +void Fonts::deinit() { + delete courrier; + delete normal; +} + +Fonts fonts; diff --git a/source/game.cpp b/source/game.cpp new file mode 100644 index 0000000..b2d924f --- /dev/null +++ b/source/game.cpp @@ -0,0 +1,957 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "input.h" +#include "net.h" +#include "qserv.h" +#include "overmind.h" +#include "random.h" +#include "packets.h" +#include "net_stuff.h" +#include "net_server.h" +#include "config.h" +#include "canvas.h" +#include "chat_text.h" +#include "texte.h" +#include "recording.h" +#include "global.h" +#include "sons.h" +#include "nglog.h" +#include "clock.h" +#include "http_request.h" +#include "game.h" +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif + +Game *game=NULL; + +Game_params::Game_params() { + name="[No name]"; + single=false; + level_up=false; + level_start=1; + allow_handicap=true; + game_end=END_NEVER; + game_end_value=0; + game_public=0; + network=true; + survivor=false; + hot_potato=false; + set_preset(PRESET_FFA); +} + +void Game_params::set_preset(Game_preset preset) { + switch(preset) { + case PRESET_FFA: + survivor=false; + break; + case PRESET_SURVIVOR: + survivor=true; + break; + case PRESET_PEACE: + normal_attack.type=ATTACK_NONE; + clean_attack.type=ATTACK_NONE; + break; + case PRESET_BLIND: + normal_attack.type=ATTACK_BLIND; + normal_attack.param=30; + clean_attack.type=ATTACK_FULLBLIND; + clean_attack.param=12; + break; + case PRESET_HOT_POTATO: + hot_potato=true; + potato_normal_attack.type=ATTACK_NONE; + potato_clean_attack.type=ATTACK_NONE; + break; + case PRESET_FULLBLIND: + normal_attack.type=ATTACK_FULLBLIND; + normal_attack.param=12; + clean_attack.type=ATTACK_FULLBLIND; + clean_attack.param=12; + break; + case PRESET_SINGLE: + normal_attack.type=ATTACK_NONE; + clean_attack.type=ATTACK_NONE; + single=true; + network=false; + level_up=true; + break; + case PRESET_SINGLE_SPRINT: + normal_attack.type=ATTACK_NONE; + clean_attack.type=ATTACK_NONE; + single=true; + network=false; + game_end=END_TIME; + game_end_value=5; + level_up=true; + break; + default: + break; + } +} + +Game::Game(Packet_gameserver *p) { + //Ok, we all know this sucks, but there supposedly are references + // to ::game in some of the things we call here and I don't + // feel like tracking them down + game=this; + the_net_version=p->version; + gameinfo=NULL; + http_failed=false; + server=false; + abort=false; + terminated=p->terminated; + net_server=NULL; + net_client = new Net_client(); + loopback_connection = NULL; + strcpy(name, p->name); + seed=p->game_seed; + paused=p->paused; + survivor = p->survivor; + hot_potato = p->hot_potato; + normal_attack = p->normal_attack; + clean_attack = p->clean_attack; + if(hot_potato) { + potato_normal_attack = p->potato_normal_attack; + potato_clean_attack = p->potato_clean_attack; + } + level_up = !p->nolevel_up; + level_start = p->level_start; + combo_min = p->combo_min; + delay_start = p->delay_start; + game_end = (End_type) p->game_end; + game_end_value = p->game_end_value; + game_public = false; // inutile pour les clients (info pour serveur seulement) + for(int i=0; iplayers.size(); i++) { + Canvas *canvas=new Canvas(seed, p->players[i]->team, p->players[i]->name, 2, 2, true, true, p->players[i]->handicap, net->server_addr(), 0, true); //On connait pas repeat, smooth, shadow mais on s'en tappe parce qu'on est pas en playback + canvas->set_id(p->players[i]->player_id); + net_list.set_player(canvas, p->players[i]->quel, false); + } + wants_moves=p->wants_moves; + net_list.syncpoint=p->syncpoint; + reset_potato(); + potato_team=p->potato_team; + single=p->single; + network=true; + frame_start = overmind.framecount; + valid_frag=false; + auto_restart=false; + record_filename[0] = 0; + slog_filename[0] = 0; + is_recording=is_slogging=false; + allow_handicap=p->allow_handicap; + net_list.reset_objectives(); +} + +Game::Game(Game_params* p) { + //Ok, we all know this sucks, but there supposedly are references + // to ::game in some of the things we call here and I don't + // feel like tracking them down + game=this; + the_net_version = Config::net_version; + if(the_net_version==20) { + if(p->hot_potato) + the_net_version = 22; + if(p->normal_attack.type!=ATTACK_LINES) + the_net_version = 22; + if(p->clean_attack.type!=ATTACK_LINES) + the_net_version = 22; + if(p->game_end>=END_POINTS) + the_net_version = 22; + } + gameinfo=NULL; + http_failed=false; + server = true; + abort=false; + terminated=false; + server_accept_player = server_accept_connection = 0; + loopback_connection = NULL; + single=p->single; + network=p->network; + net_server = new Net_server(); + net_client = new Net_client(); + strncpy(name, p->name, sizeof(name)); + name[sizeof(name)-1]=0; + seed = ugs_random.get_seed(); + paused = false; + delay_start = 0; + survivor = p->survivor; + hot_potato = p->hot_potato; + normal_attack = p->normal_attack; + clean_attack = p->clean_attack; + if(hot_potato) { + potato_normal_attack = p->potato_normal_attack; + potato_clean_attack = p->potato_clean_attack; + } + level_up = p->level_up; + level_start = p->level_start; + combo_min = 2; + game_end = p->game_end; + game_end_value = p->game_end_value; + if(game_end==END_FRAG && !any_attack()) { + game_end=END_NEVER; + game_end_value=0; + } + if(game_end == END_TIME) + game_end_value = game_end_value * 6000; //minutes->centiseconds + if(game_end == END_POINTS) + game_end_value = game_end_value * 1000; //Kpoints->points + game_public = p->game_public; + if(!single) { + paused = true; // demarre la game sur pause + delay_start=500; + } + frame_start = overmind.framecount; + valid_frag=false; + wants_moves=true; + reset_potato(); + auto_restart=false; + record_filename[0] = 0; + slog_filename[0] = 0; + is_recording=is_slogging=false; + allow_handicap=p->allow_handicap; + net_list.reset_objectives(); +} + +Game::~Game() { + stop_stuff(); + if(gameinfo) + delete gameinfo; + if(net_client) + delete net_client; + if(net_server) + delete net_server; + if(stack.size()) + msgbox("Game::~Game: stack.size should be 0\n"); + stack.deleteall(); + //Oh well... + game = NULL; +} + +void Game::stop_stuff() { + //Can be called repetitively for the same game + //Stop recording + if(net_server && recording) { + //Write game summary + recording->end_multi(); + Byte *rec=recording->all_output; + if(rec) { + Dword size=recording->all_output_size; + while(size) { + Textbuf buf; + Dword part_size=min(size, 100); + Http_request::base64encode(rec, buf, part_size); + log_step("game_rec\t%s", buf.get()); + size-=part_size; + rec+=part_size; + } + } + net_server->stop_multi_recording(); + } + char salt[33]; + salt[0]='A'; + salt[1]='n'; + salt[2]='r'; + salt[3]='y'; + salt[4]=' '; + salt[5]='i'; + salt[6]='s'; + salt[7]=' '; + salt[8]='a'; + salt[9]=' '; + salt[10]=0; + if(is_slogging) { + salt[10]='s'; + salt[11]='m'; + salt[12]='a'; + salt[13]='r'; + salt[14]='t'; + salt[15]=','; + salt[16]=' '; + salt[17]='b'; + salt[18]='e'; + salt[19]='e'; + salt[20]='u'; + salt[21]='t'; + salt[22]='i'; + salt[23]='f'; + salt[24]='u'; + salt[25]='l'; + salt[26]=' '; + salt[27]='l'; + salt[19]='a'; + salt[28]='a'; + salt[29]='d'; + salt[30]='y'; + salt[31]='.'; + salt[32]=0; + log_finalize(salt); + memset(salt, 5, sizeof(salt)); + } +} + +void Game::restart() { + if(!server) + return; + if(!terminated) + return; + if(quitting) + return; + //Wait until everybody is gone + if(!net_list.all_gone()) + return; + + msgbox("Game::restart: restarting game now.\n"); + //Make all currently joined connections not joined + int i; + if(net->active) { + for(i=0; iconnections.size(); i++) { + Net_connection *nc = net->connections[i]; + if(nc->joined && nc!=loopback_connection) + nc->joined=false; + } + } + //Drop all players + for(i=0; iconnections.size(); i++) { + Net_connection *nc=net->connections[i]; + if(nc && nc!=loopback_connection) { + char st[64]; + Net::stringaddress(st, nc->address(), nc->getdestport()); + log_step("connect\t%u\t%s", nc->id(), st); + } + } +} + +void Game::reset_potato() { + int i; + potato_team = previous_potato_team = 255; + for(i=0; i=MAXTEAMS) + rnd=0; + } + order_taken[rnd]=true; + potato_order[team]=rnd; + } +} + +Byte Game::next_potato_team() { + Byte ret=255; + int i, j; + for(i=0; iidle<2 && c->color==potato_order[i]) { + ret=potato_order[i]; + potato_order[i]=255; + //Break out of both loops + i=MAXTEAMS; + j=MAXPLAYERS; + break; + } + } + } + if(ret==255) { + new_potato_order(); + for(i=0; iidle<2 && c->color==potato_order[i]) { + ret=potato_order[i]; + potato_order[i]=255; + //Break out of both loops + i=MAXTEAMS; + j=MAXPLAYERS; + break; + } + } + } + } + if(ret!=255) + previous_potato_team=ret; + return ret; +} + +void Game::got_potato(Byte team, int lines) { + int i; + for(i=0; icolor==team) { + if(c->islocal()) { + c->add_text_scroller(ST_YOUGOTPOTATO1, 4, -20); + c->add_text_scroller(ST_YOUGOTPOTATO2, 4); + Sfx stmp(sons.potato_get, 0, -1200, 0, 44100); + } + c->potato_lines=0; + c->team_potato_lines=0; + c->team_potato_linestot=lines; + if(c->inter && !c->small_watch) { + c->z_lines->disable(); + c->z_potatolines->enable(); + c->z_linestot->disable(); + c->z_potatolinestot->enable(); + } + } + } + char st[1024]; + if(chat_text) { + net_list.team2name(team, st); + strcat(st, ST_GETSPOTATO); + message(-1, st); + sprintf(st, ST_CLEARBOBLINES, lines); + message(-1, st); + } +} + +void Game::done_potato(Byte team) { + int i; + for(i=0; icolor==team) { + if(c->islocal() && !(c->dying || c->idle>=2)) { + c->add_text_scroller(ST_YOUGOTRIDOFPOTATO1, 4, -40); + c->add_text_scroller(ST_YOUGOTRIDOFPOTATO2, 4, -20); + Sfx stmp(sons.potato_rid, 0, -300, 0, 11025); + } + int x, y; + for(y = 0; y < 36; y++) + for(x = 0; x < 18; x++) { + if(c->occupied[y][x]) { + if(c->blinded[y][x]) { + c->dirted[y][x] = 2; + c->bflash[y][x] = 24; + } + c->blinded[y][x] = 0; + } + } + if(c->islocal()) + c->should_remove_bonus=true; + if(c->inter && !c->small_watch) { + c->z_lines->enable(); + c->z_potatolines->disable(); + c->z_linestot->enable(); + c->z_potatolinestot->disable(); + } + } + } +/* char st[1024]; + if(chat_text) { + net_list.team2name(team, st); + strcat(st, ST_DONEPOTATO); + message(-1, st); + }*/ +} + +void Game::check_potato() { + int i; + Canvas *c; + if(!game->hot_potato) + return; + //Clients only have to obey the server's Packet_serverpotato + Packet_serverpotato *p=(Packet_serverpotato *) peekpacket(P_SERVERPOTATO); + if(p) { + if(potato_team!=255 && p->team==255) + done_potato(potato_team); + potato_team=p->team; + if(potato_team!=255) + potato_lines[potato_team]=p->potato_lines; + removepacket(); + if(potato_team!=255) { + got_potato(potato_team, potato_lines[potato_team]); + } + } + if(!server) + return; + //Server stuff from this point on... + if(potato_teamcolor==potato_team) { + total+=c->potato_lines; + if(c->idle<2) + nb_player_alive++; + if(c->idle<3) + nb_player++; + } + } + if(total>=potato_lines[potato_team] || nb_player_alive==0) { + if(nb_player==0) + potato_lines[potato_team]=2; + if(nb_player_alive) { + //At least one alive, next time it will be tougher :) + potato_lines[potato_team]++; + } + //Potato team is done with the potato, signal it + Packet_serverpotato p; + p.team=255; + p.potato_lines=0; + net->dispatch(&p, P_SERVERPOTATO, loopback_connection); + if(net_server) + net_server->record_packet(&p); + char *reason="all_gone"; + if(nb_player) + reason="all_died"; + if(nb_player_alive) + reason="cleared_lines"; + log_step("potato_done\t%s\t%s", log_team(potato_team), reason); + done_potato(potato_team); + potato_team=255; + } + } + //Don't pass potato if not started or not competitive + if(game->delay_start) + return; + if(!net_list.competitive()) + return; + //... or terminated! + if(terminated || net_list.end_signaled) + return; + last_given_potato++; + if(potato_team==255 && last_given_potato>=200) { + //Should give the potato to somebody now + Byte to_team=next_potato_team(); + if(to_team!=255) { + last_given_potato=0; + //Give the potato to the chosen team + Packet_serverpotato p; + p.team=to_team; + p.potato_lines=potato_lines[to_team]; + net->dispatch(&p, P_SERVERPOTATO, loopback_connection); + if(net_server) + net_server->record_packet(&p); + got_potato(to_team, potato_lines[to_team]); + potato_team=to_team; + log_step("potato_given\t%s\t%i", log_team(to_team), potato_lines[to_team]); + } + } +} + +void Game::clientpause() { + Packet_clientpause p; + net->sendtcp(&p); +} + +void Game::stackpacket(Packet *p) { + stack.add(p); +} + +Packet *Game::peekpacket(Byte type) { + if(stack.size()) { + if(stack[0]->packet_id == type || type==255) + return stack[0]; + else + return NULL; + } else { + return NULL; + } +} + +void Game::removepacket() { + if(stack.size()) { + delete stack[0]; + stack.remove(0); + } +} + +void Game::count_playing_time() { + if(!paused) + stats[GS::PLAYING_TIME].add(1); +} + +int Game::net_version() { + if(playback && playback->old_mode) + return 20; + else + return the_net_version; +} + +bool Game::any_attack() { + if(normal_attack.type!=ATTACK_NONE || clean_attack.type!=ATTACK_NONE) + return true; + if(hot_potato && (potato_normal_attack.type!=ATTACK_NONE || potato_clean_attack.type!=ATTACK_NONE)) + return true; + return false; +} + +void Game::addgameinfo(Textbuf *tb) { + tb->append("name %s\n", name); + tb->append("version %i\n", net_version()); + tb->append("port %i\n", config.info.port_number); + tb->append("status/started %i\n", !delay_start? 1:0); + tb->append("status/terminated %i\n", terminated? 1:0); + tb->append("status/autorestart %i\n", auto_restart? 1:0); + tb->append("status/running_time %i\n", overmind.framecount); + tb->append("stats/playing_time %i\n", stats[GS::PLAYING_TIME].get_value()); + tb->append("stats/round_number %i\n", stats[GS::ROUND_NUMBER].get_value()); + int type=0; + if(survivor) + type=1; + if(normal_attack.type==ATTACK_NONE) + type=2; + if(normal_attack.type==ATTACK_BLIND || normal_attack.type==ATTACK_FULLBLIND) + type=3; + if(hot_potato) + type=4; + if(single) + type=5; + tb->append("rules/type %i\n", type); + tb->append("rules/survivor %i\n", survivor? 1:0); + tb->append("rules/hot_potato %i\n", hot_potato? 1:0); + tb->append("rules/attacks/normal %i %i\n", normal_attack.type, normal_attack.param); + tb->append("rules/attacks/clean %i %i\n", clean_attack.type, clean_attack.param); + if(hot_potato) { + tb->append("rules/attacks/potato_normal %i %i\n", potato_normal_attack.type, potato_normal_attack.param); + tb->append("rules/attacks/potato_clean %i %i\n", potato_clean_attack.type, potato_clean_attack.param); + } + tb->append("rules/levelstart %i\n", level_start); + tb->append("rules/levelup %i\n", level_up? 1:0); + tb->append("rules/mincombo %i\n", combo_min); + tb->append("rules/allowhandicap %i\n", allow_handicap? 1:0); + tb->append("end/type %i\n", game_end); + tb->append("end/value %i\n", game_end_value); + int player = 0; + for(int i=0; iappend("players/%i/name %s\n", player, c->name); + tb->append("players/%i/team %i\n", player, c->color); + tb->append("players/%i/status %i\n", player, c->idle); + tb->append("players/%i/handicap %i\n", player, c->handicap); + int maxcombo=0; + int combo_count=0; + int combo; + for(combo=CS::CLEAR00; combo<=CS::CLEAR13; combo++) { + int cc=c->stats[combo].get_value(); + if(cc) { + combo_count=cc; + switch(combo) { + case CS::CLEAR00: maxcombo=1; break; + case CS::CLEAR01: maxcombo=2; break; + case CS::CLEAR02: maxcombo=3; break; + case CS::CLEAR03: maxcombo=4; break; + case CS::CLEAR04: maxcombo=5; break; + case CS::CLEAR05: maxcombo=6; break; + case CS::CLEAR06: maxcombo=7; break; + case CS::CLEAR07: maxcombo=8; break; + case CS::CLEAR08: maxcombo=9; break; + case CS::CLEAR09: maxcombo=10; break; + case CS::CLEAR10: maxcombo=11; break; + case CS::CLEAR11: maxcombo=12; break; + case CS::CLEAR12: maxcombo=13; break; + case CS::CLEAR13: maxcombo=14; break; + } + } + } + for(combo=CS::CLEAR14; combo<=CS::CLEAR20; combo++) { + int cc=c->stats[combo].get_value(); + if(cc) { + combo_count=cc; + switch(combo) { + case CS::CLEAR14: maxcombo=15; break; + case CS::CLEAR15: maxcombo=16; break; + case CS::CLEAR16: maxcombo=17; break; + case CS::CLEAR17: maxcombo=18; break; + case CS::CLEAR18: maxcombo=19; break; + case CS::CLEAR19: maxcombo=20; break; + case CS::CLEAR20: maxcombo=21; break; + } + } + } + if(maxcombo) + tb->append("players/%i/maxcombo %i %i\n", player, maxcombo, combo_count); + if(c->stats[CS::PLAYING_TIME].get_value()) + tb->append("players/%i/ppm %u\n", player, c->stats[CS::PPM].get_value()); + tb->append("players/%i/playing_time %u\n", player, c->stats[CS::PLAYING_TIME].get_value()); + if(!game->delay_start) { + tb->append("players/%i/frags %i\n", player, c->stats[CS::FRAG].get_value()); + tb->append("players/%i/deaths %i\n", player, c->stats[CS::DEATH].get_value()); + tb->append("players/%i/lines %i\n", player, c->stats[CS::LINESTOT].get_value()); + tb->append("players/%i/score %i\n", player, c->stats[CS::SCORE].get_value()); + } + player++; + } + } +} + +void Game::buildgameinfo() { + gameinfo->add_data("postgame\n"); + Textbuf tb; + addgameinfo(&tb); + gameinfo->add_data(tb.get()); +} + +void Game::sendgameinfo(bool quit) { + char st[1024]; + if(!network || !game_public || !server) + return; + if(gameinfo) + delete gameinfo; + gameinfo=new Qserv(); + char *msg = net->failed(); + if(msg) { + sprintf(st, ST_NETWORKERRORLOOKINGBOB, msg); + message(-1, st, true, false, true); + message(-1, ST_GAMENOTPUBLIC, true, false, true); + http_failed = true; + delete gameinfo; + gameinfo = NULL; + } else { + if(quit) + gameinfo->add_data("deletegame\n"); + else + buildgameinfo(); + gameinfo->send(); + } +} + +void Game::stepgameinfo() { + if(gameinfo) { + if(gameinfo->done()) { + const char *status=gameinfo->get_status(); + if(status==NULL || (strcmp(status, "Game added") && strcmp(status, "Game updated"))) { + message(-1, ST_INVALIDSERVERRESPONSE, true, false, true); + /*message(-1, ST_GAMENOTPUBLIC, true, false, true); + http_failed = true; + game_public = false;*/ + } + delete gameinfo; + gameinfo=NULL; + } + } +} + +bool Game::gameinfo_completed() const { + return gameinfo ? false:true; +} + +void Game::endgame() { + terminated = true; +} + +void Game::prepare_recording(const char *fn) { + recording = new Recording(); + is_recording=true; + char st[1024]; + char nom[1024]; + if(fn) { + strncpy(record_filename, fn, 1023); + record_filename[1023] = 0; + } + strcpy(nom, record_filename); + //Remove .rec if present + int len = strlen(nom); + if(len>=4) + if(!stricmp(".rec", &nom[len-4])) + nom[len-4] = 0; + //When restarting and recording, auto-increment file name + static int record_num = 0; + if(auto_restart || !fn) { + sprintf(st, "%04i", record_num++); + strcat(nom, st); + } + strcat(nom, ".rec"); // ajoute .rec + if(!recording->create(nom)) { + sprintf(st, ST_GAMENOTRECORDEDAS, nom); + message(-1, st, true, false, true); + msgbox("Game::prepare_recording: Warning: could not create demo file\n"); + delete recording; // si la creation du fichier a pas marcher + recording=NULL; + } + else { + sprintf(st, ST_GAMERECORDINGAS, nom); + message(-1, st, true, false, true); + Packet_gameserver p; + Net_pendingjoin::load_packet_gameserver(&p); + recording->start_for_multi(&p); + } +} + +void Game::prepare_logging(const char *filename) { + char *the_file; + the_file=slog_filename; + char st[1024]; + char nom[1024]; + if(filename) { + strncpy(the_file, filename, 1023); + the_file[1023] = 0; + } + strcpy(nom, the_file); + //Remove .log if present + int len = strlen(nom); + if(len>=4) + if(!stricmp(".log", &nom[len-4])) + nom[len-4] = 0; + //When restarting and logging, auto-increment file name + static int slog_num = 0; + int *the_num; + the_num=&slog_num; + if(auto_restart || !filename) { + sprintf(st, "%04i", (*the_num)++); + strcat(nom, st); + } + strcat(nom, ".log"); + if(!log_init(nom)) { + sprintf(st, ST_GAMENOTLOGGEDAS, nom); + message(-1, st, true, false, true); + msgbox("Game::prepare_logging: Warning: could not create log file\n"); + } + else { + void (*the_log_step)(const char *st, ...); + is_slogging=true; + the_log_step=log_step; + sprintf(st, ST_GAMELOGGINGAS, nom); + message(-1, st, true, false, true); + //Begin log output here + the_log_step("info\tlog_standard\tngLog"); + the_log_step("info\tlog_version\t1.2"); + the_log_step("info\tlog_info_url\thttp://www.NetGamesUSA.com/ngLog/"); + the_log_step("info\tgame_name\tQuadra"); + the_log_step("info\tgame_author\tLudus Design"); + the_log_step("info\tgame_author_url\thttp://ludusdesign.com"); + char *os; + #ifdef UGS_DIRECTX + os="Windows"; + #endif + #ifdef UGS_LINUX + os="Linux"; + #endif + the_log_step("info\tgame_version\t%i.%i.%i\t%s", Config::major, Config::minor, Config::patchlevel, os); + the_log_step("info\tgame_decoder_ring_url\thttp://ludusdesign.com/decoder_ring.txt"); + //Even better than what the NetGames guys do! :) + Dword addr=INADDR_LOOPBACK; + for(int i=0; ihost_adr.size(); i++) { + Dword a=net->host_adr[i]; + //Ignore 192.168.x.x unless we find something "better" + if(a>>16==49320) + if(addr>>16!=49320 && addr!=INADDR_LOOPBACK) + continue; + addr=a; + } + char st[64]; + Net::stringaddress(st, addr); + the_log_step("info\tserver_address\t%s", st); + the_log_step("info\tserver_port\t%i", config.info.port_number); + char *abs_time=Clock::absolute_time(); + the_log_step("info\tabsolute_time\t%s", abs_time); + + the_log_step("game_init\t%s", abs_time); + the_log_step("game_param\tname\t%s", name); + char *game_type="ffa"; + if(survivor) + game_type="survivor"; + if(normal_attack.type==ATTACK_NONE && clean_attack.type==ATTACK_NONE) + game_type="peace"; + if(normal_attack.type==ATTACK_BLIND || normal_attack.type==ATTACK_FULLBLIND) + game_type="blind"; + if(hot_potato) + game_type="hot_potato"; + if(single) { + game_type="single"; + if(game_end==END_TIME) + game_type="single_sprint"; + } + the_log_step("game_param\tgame_type\t%s", game_type); + the_log_step("game_param\tsurvivor\t%s", survivor? "true":"false"); + the_log_step("game_param\thot_potato\t%s", hot_potato? "true":"false"); + the_log_step("game_param\tnormal_attack\t%s\t%i", normal_attack.log_type(), normal_attack.param); + the_log_step("game_param\tclean_attack\t%s\t%i", clean_attack.log_type(), clean_attack.param); + if(hot_potato) { + the_log_step("game_param\tpotato_normal_attack\t%s\t%i", potato_normal_attack.log_type(), potato_normal_attack.param); + the_log_step("game_param\tpotato_clean_attack\t%s\t%i", potato_clean_attack.log_type(), potato_clean_attack.param); + } + the_log_step("game_param\tlevel_up\t%s", level_up? "true":"false"); + the_log_step("game_param\tlevel_start\t%i", level_start); + the_log_step("game_param\tallow_handicap\t%s", allow_handicap? "true":"false"); + char *end_type="unknown"; + switch(game_end) { + case END_NEVER: end_type="never"; break; + case END_FRAG: end_type="frags"; break; + case END_TIME: end_type="time"; break; + case END_POINTS: end_type="points"; break; + case END_LINES: end_type="lines"; break; + default: break; + } + the_log_step("game_param\tgame_end\t%s\t%i", end_type, game_end_value); + } +} + +int Game::get_multi_level() { + if(playback) + return playback->multi_level; + else + return config.info.multi_level; +} + +void Game::set_seed(Packet_serverrandom *p) { + msgbox("Game::set_seed\n"); + int i; + for(i=0; irnd.set_seed(p->seed); + } + } + seed=p->seed; +} + +char *Game::get_motd() { + if(net_list.motd[0]) + return net_list.motd; + else + return NULL; +} diff --git a/source/game_menu.cpp b/source/game_menu.cpp new file mode 100644 index 0000000..dca6c47 --- /dev/null +++ b/source/game_menu.cpp @@ -0,0 +1,534 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "input.h" +#include "config.h" +#include "multi_player.h" +#include "game.h" +#include "net_stuff.h" +#include "chat_text.h" +#include "texte.h" +#include "global.h" +#include "menu_base.h" +#include "quadra.h" +#include "menu.h" +#include "canvas.h" +#include "main.h" +#include "nglog.h" +#include "clock.h" +#include "game_menu.h" + +int Create_game::game_end_y=0; + +Create_game::Create_game(Bitmap *bit, Font *font, Font *font2, const Palette& p, bool pnet_game, bool plocal_net) { + pal = p; + bit_ = bit; + inter->set_font(font, false); + (void)new Zone_bitmap(inter, bit, 0, 0); + net_game=pnet_game; + local_net = plocal_net; + + int y=20, inc=30; + Zone *z; + + (void)new Zone_text(inter, ST_CREATEGAMETITLE, y); + y+=inc; + y+=inc; + + strcpy(name, config.info.game_name); + game_public = config.info.game_public; + z=new Zone_text(fteam[7], inter, ST_ENTERGAMENAME, 20, y); + z=new Zone_text_input(inter, pal, name, 32, 20+z->w+10, y, 320-10-20-z->w-10); + int name_x=z->x; + //This one is created here to line up stuff + z=new Zone_text(fteam[7], inter, ST_GAMESPEED, 340, y+inc); + int public_x=340+z->w+10; + if(net_game) { + if(!local_net) { + (void)new Zone_text(fteam[7], inter, ST_ALLOWPUBLICGAME, 340, y); + Zone_state_text *temp = new Zone_state_text2(inter, &game_public, public_x, y); + temp->add_string(ST_NO); + temp->add_string(ST_YES); + } + } + y+=inc; + + z=new Zone_text(fteam[7], inter, ST_SELECTGAMETYPE, 20, y); + selected = config.info.game_type; + { + Zone_state_text2 *temp = new Zone_state_text2(inter, &selected, name_x, y); + temp->add_string(ST_GAMETYPE1); + temp->add_string(ST_GAMETYPE2); + temp->add_string(ST_GAMETYPE4); + temp->add_string(ST_GAMETYPE5); + temp->add_string(ST_GAMETYPE3); + game_type = temp; + } + game_type->add_watch(this); + + level_up = config.info.level_up; + { + Zone_state_text2 *temp = new Zone_state_text2(inter, &level_up, public_x, y); + temp->add_string(ST_GAMELEVELUP1); + temp->add_string(ST_GAMELEVELUP2); + } + + y+=inc; + + int i; + for(i=0; i<9; i++) { + game_desc[i] = new Zone_text(fteam[3], inter, "", 40, y); + y+=inc/2; + } + game_desc[9]=NULL; //Only 9 lines displayed finally... + y+=inc/2; + game_descriptions=new Stringtable(ST_GAMETYPEDESCRIPTIONS); + + game_end = config.info.game_end; + game_end_value = config.info.game_end_value; + game_end_watch = NULL; + game_end_selector = NULL; + game_end_text = NULL; + game_end_num = NULL; + game_end_y=y; + recreate_game_end(); + y+=inc; + y+=inc; + + z=new Zone_text(fteam[7], inter, ST_RECORDGAME, 20, y); + record_game = 0; + Zone_state_text2 *temp = new Zone_state_text2(inter, &record_game, 20+z->w+10, y); + temp->add_string(ST_NO); + temp->add_string(ST_YES); + record_watch = temp; + record_watch->add_watch(this); + record_zone = temp; + strcpy(record_name, Clock::absolute_time()); + z_record_name = NULL; + y+=inc; + + slog = 0; + if(!Config::xtreme) { + (void)new Zone_text(fteam[7], inter, ST_SLOG, 20, y); + temp = new Zone_state_text2(inter, &slog, 20+z->w+10, y); + temp->add_string(ST_NO); + temp->add_string(ST_YES); + slog_watch = temp; + slog_watch->add_watch(this); + slog_zone = temp; + z_slog_name = NULL; + strcpy(slog_name, Clock::absolute_time()); + z_slog_name = NULL; + y+=inc; + } + else { + slog_watch = NULL; + slog_zone = NULL; + } + + save = new Zone_text_button2(inter, bit, font2, ST_SAVESETTING, 20, 450); + start = new Zone_text_button2(inter, bit, font2, ST_STARTGAME, 400, 450); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); + + notify(); +} + +Create_game::~Create_game() { + if(game_end_watch) + game_end_watch->remove_watch(this); + if(game_type) + game_type->remove_watch(this); + if(record_watch) + record_watch->remove_watch(this); + if(slog_watch) + slog_watch->remove_watch(this); + if(game_descriptions) + delete game_descriptions; +} + +void Create_game::recreate_game_end() { + static int old_selected=-1; + //If changed to peace and not endnever, adjust for missing option + if(selected==2 && old_selected!=2 && game_end) + game_end--; + //Same thing but the other way around + if(selected!=2 && old_selected==2 && game_end) + game_end++; + old_selected=selected; + if(game_end_watch) + game_end_watch->remove_watch(this); + if(game_end_selector) + delete game_end_selector; + int y=game_end_y; + Zone *z; + z=new Zone_text(fteam[7], inter, ST_SETGAMEEND, 20, y); + Zone_state_text2 *temp = new Zone_state_text2(inter, &game_end, z->x+z->w+10, y); + temp->add_string(ST_GAMEEND1); + if(selected!=2) //endfrags only when !peace + temp->add_string(ST_GAMEEND2); + temp->add_string(ST_GAMEEND3); + temp->add_string(ST_GAMEEND4); + //temp->add_string(ST_GAMEEND5); + game_end_selector = temp; + game_end_watch = temp; + game_end_watch->add_watch(this); + z=temp; + if(game_end_text) + delete game_end_text; + if(game_end_num) + delete game_end_num; + int num_x=z->x+z->w+10, text_x=num_x+50+10; + switch(game_end+(selected==2 && game_end? 1:0)) { + case 1: + game_end_num = new Zone_input_numeric(inter, &game_end_value, 5, 1, 9999, pal, num_x, y, 50); + game_end_text = new Zone_text(fteam[7], inter, ST_GAMEENDFRAG, text_x, y); + break; + case 2: + game_end_num = new Zone_input_numeric(inter, &game_end_value, 5, 1, 9999, pal, num_x, y, 50); + game_end_text = new Zone_text(fteam[7], inter, ST_GAMEENDMINUTE, text_x, y); + break; + case 3: + game_end_num = new Zone_input_numeric(inter, &game_end_value, 5, 1, 99999, pal, num_x, y, 50); + char st[64]; + sprintf(st, "x 1000 %s", ST_GAMEENDSCORE); + game_end_text = new Zone_text(fteam[7], inter, st, text_x, y); + break; + case 4: + game_end_num = new Zone_input_numeric(inter, &game_end_value, 5, 1, 99999, pal, num_x, y, 50); + game_end_text = new Zone_text(fteam[7], inter, ST_GAMEENDLINES, text_x, y); + break; + default: + game_end_text = NULL; + game_end_num = NULL; + break; + } +} + +void Create_game::notify() { + video->need_paint = 2; + int base=selected*10; + int i; + for(i=0; i<10; i++) + if(game_desc[i]) + game_desc[i]->set_text(game_descriptions->get(base+i)); + recreate_game_end(); + if(record_watch) { + if(z_record_name) + delete z_record_name; + if(record_game == 1) { + int x=record_zone->x+record_zone->w+10; + z_record_name = new Zone_text_input(inter, pal, record_name, 32, x, record_zone->y, 220); + } + else + z_record_name = NULL; + } + if(slog_watch) { + if(z_slog_name) + delete z_slog_name; + if(slog == 1) { + int x=slog_zone->x+slog_zone->w+10; + z_slog_name = new Zone_text_input(inter, pal, slog_name, 32, x, slog_zone->y, 220); + } + else + z_slog_name = NULL; + } +} + +void Create_game::step() { + Menu::step(); + if(input->quel_key == 1 || result==cancel || quitting) { + input->quel_key = 0; + ret(); + } + if(result==start) { + call(new Fade_in(pal)); + bool publi = false; + if(net_game && !local_net) // force une game non-internet a "public=false" toujours + publi = game_public? true:false; + if(!name[0]) + strcpy(name, ST_GAMENONAME); + Game_params p; + p.name=name; + switch(selected) { + case 0: p.set_preset(PRESET_FFA); break; + case 1: p.set_preset(PRESET_SURVIVOR); break; + case 2: p.set_preset(PRESET_PEACE); break; + case 3: p.set_preset(PRESET_BLIND); break; + case 4: p.set_preset(PRESET_HOT_POTATO); break; + } + p.level_up=level_up? false:true; + p.level_start=1; + p.allow_handicap=true; + p.game_end=(End_type) (game_end+(selected==2 && game_end? 1:0)); + p.game_end_value=game_end_value; + p.game_public=publi; + p.network=net_game; + (void)new Game(&p); + if(record_game == 1) + game->prepare_recording(record_name); + if(slog==1) + game->prepare_logging(slog_name); + call(new Create_game_start(pal, bit_, inter->font)); + } + if(result==save) + save_setting(); +} + +void Create_game::save_setting() { + strcpy(config.info.game_name, name); + config.info.game_type = selected; + config.info.level_up = level_up; + config.info.level_start = 1; + config.info.combo_min = 0; + config.info.game_end = game_end; + config.info.game_end_value = game_end_value; + config.info.game_public = game_public; + config.write(); +} + +Create_game_start::Create_game_start(const Palette &pal_, Bitmap *bit, Font *font): + pal(pal_) { + bit_ = bit; + font_ = font; + sprintf(st, ST_GAMEBOBCREATED, game->name); + message(-1, st, true, false, true); +} + +Create_game_start::~Create_game_start() { + Packet_bye p; + net->sendtcp(&p); + net->stop_client(); + net->stop_server(); + if(game) { + delete game; + if(chat_text) + chat_text->clear(); + } + msgbox("Create_game_start::~Create_game_start\n"); +} + +void Create_game_start::init() { + char *tube; + tube = net->failed(); + if(tube) { + exec(new Menu_net_problem(tube, ST_CREATESERVERFAILED, bit_, font_)); + return; + } + + game->loopback_connection=net->start_loopback_client(); + game->loopback_connection->joined=true; + game->loopback_connection->trusted=true; + ret(); + + if(game->game_public) { + call(new Create_game_end(pal, bit_, font_)); + } + //We assume video_is_dumb==true means dedicated + if(video_is_dumb) { + call(new Menu_do_nothing()); + } + else { + call(new Call_setfont(pal, new Multi_player_launcher())); + call(new Fade_out(pal)); + } +} + +Create_game_end::Create_game_end(const Palette &pal_, Bitmap *bit, Font *font): pal(pal_) { + inter->set_font(font, false); + new Zone_bitmap(inter, bit, 0, 0); + new Zone_text(fteam[7], inter, ST_UPDATINGGAMESERVER, 140); + new Zone_text(fteam[7], inter, ST_ONEMOMENTPLEASE, 180); + cancel = new Zone_text_button2(inter, bit, font, ST_CLICKTOCANCEL, 250); +} + +Create_game_end::~Create_game_end() { +} + +void Create_game_end::init() { + if(!game->game_public) { // si partie pu publique, abort tout! + ret(); + return; + } + net->stop_server(); //Disconnect all clients + Menu::init(); + call(new Fade_in(pal)); + game->sendgameinfo(true); +} + +void Create_game_end::step() { + Menu::step(); + game->stepgameinfo(); + if(result==cancel || game->gameinfo_completed()) { + exec(new Fade_out(pal)); + } +} + +Join_game::Join_game(Bitmap *bit, Font *font, Font *font2, const Palette& p, const char *n, Dword sa, int sport, bool prejoin) { + rejoin=prejoin; + address = sa; + port = sport; + pal = p; + bit_ = bit; + font2_ = font2; + inter->set_font(font, false); + (void)new Zone_bitmap(inter, bit, 0, 0); + (void)new Zone_text(inter, ST_JOINGAMETITLE, 20); + if(n) { + sprintf(st, ST_WAITJOINGAME, n); + } else { + char tube[256], tube2[256]; + Net::stringaddress(tube, sa); + if(port) { + sprintf(tube2, "%s:%i", tube, port); + sprintf(st, ST_WAITJOINGAME3, tube2); + } else { + sprintf(st, ST_WAITJOINGAME3, tube); + } + } + msgbox("Join_game::Join_game: address=%x, port=%i, n=[%s]\n", address, port, n? n:"NULL"); + status = new Zone_text(fteam[7], inter, st, 210); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); + + eping=NULL; + delay = 0; +} + +void Join_game::init() { + Menu::init(); + if(!rejoin) { + net->start_client(address, port); + char *tube = net->failed(); + if(tube) { + exec(new Menu_net_problem(tube, ST_JOINGAMEFAILED, bit_, inter->font)); + } + } +} + +void Join_game::step() { + Menu::step(); + if(input->quel_key == 1 || result==cancel) { + input->quel_key = 0; + ret(); + } + bool connect = net->connected(); + char *tube = net->failed(); + if(tube) { + exec(new Menu_net_problem(tube, ST_JOINGAMEFAILED, bit_, inter->font)); + return; + } + if(connect && !eping) { + status->set_text(ST_WAITJOINGAME2); + video->need_paint = 2; + eping=new Exec_ping(&pac, P_GAMESERVER, this); + } + if(eping && !connect) { + status->set_text(ST_NOTQUADRASERVER); + (void)new Zone_text(fteam[7], inter, ST_JOINREFUSED2, 240); + video->need_paint = 2; + delete eping; + eping=NULL; + } + delay++; + if(delay == 4000) { + (void)new Zone_text(fteam[7], inter, ST_WAITJOINGAMEDELAY, 270); + } +} + +void Join_game::net_call(Packet *p2) { + Packet_gameserver *p=(Packet_gameserver *) p2; + bool ok = true; + if(!p->accepted) { + status->set_text(ST_JOINREFUSED); + (void)new Zone_text(fteam[7], inter, ST_JOINREFUSED2, 240); + video->need_paint = 2; + ok = false; + } + //Hot potato and Peace games are created with net_version==22 + // but we leave net_version at 20 to remain compatible + // with other game types, so here we allow joining + // games of net_version==22. + if(p->version < 20 || (p->version > Config::net_version && p->version != 22)) { + if(p->version < Config::net_version) { + sprintf(st, ST_JOINOLDERVERSION, p->version); + status->set_text(st); + sprintf(st, ST_JOINOLDERVERSION2, Config::net_version); + (void)new Zone_text(fteam[7], inter, st, 240); + } else { + sprintf(st, ST_JOINNEWERVERSION, p->version); + status->set_text(st); + (void)new Zone_text(fteam[7], inter, ST_JOINNEWERVERSION2, 240); + } + video->need_paint = 2; + ok = false; + } + if(ok) { + (void)new Game(p); + sprintf(st,ST_GAMEBOBJOINED, game->name); + message(-1, st); + exec(new Join_download(bit_, inter->font, font2_, pal, rejoin)); + } +} + +Join_game::~Join_game() { + if(net->connected()) { + Packet_bye p; + net->sendtcp(&p); + net->stop_client(); + } + if(eping) + delete eping; + if(game) { + delete game; + if(chat_text) + chat_text->clear(); + } +} + +Join_download::Join_download(Bitmap *bit, Font *font, Font *font2, const Palette& p, bool prejoin) { + rejoin=prejoin; + font2_ = font2; + pal = p; + bit_ = bit; + nb_total = game->net_list.size(); + nb_current = nb_percent = 0; + inter->set_font(font, false); + new Zone_bitmap(inter, bit, 0, 0); + new Zone_text(inter, ST_JOINGAMETITLE, 120); + new Zone_text(fteam[7], inter, ST_JOINDOWNLOADING, 210); + new Zone_text(fteam[7], inter, "%", 340, 240); + new Zone_text_field(inter, &nb_percent, 290, 240, 40); +} + +Join_download::~Join_download() { +} + +void Join_download::step() { + if(!net->connected()) { + exec(new Menu_net_problem(ST_DOWNLOADDECONNECT1, ST_DOWNLOADDECONNECT2, bit_, inter->font)); + return; + } + nb_current=0; + int i; + for(i=0; inet_list.get(i); + if(c && !c->wait_download) + nb_current++; + } + if(nb_total) + nb_percent = nb_current*100/nb_total; + if(nb_current == nb_total) { + nb_percent = 100; + if(!rejoin) { + exec(new Fade_in(pal)); + call(new Call_setfont(pal, new Multi_player_launcher())); + call(new Fade_out(pal)); + } + else { + exec(new Call_setfont(pal, new Multi_player_launcher())); + call(new Fade_out(pal)); + } + } +} diff --git a/source/global.cpp b/source/global.cpp new file mode 100644 index 0000000..758d4a6 --- /dev/null +++ b/source/global.cpp @@ -0,0 +1,93 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "types.h" +#include "sound.h" +#include "config.h" +#include "nglog.h" +#include "global.h" + +char st[4096] = {""}; + +char quadradir[1024] = {"."}; + +const char *english_teams[MAXTEAMS] = { + "Orange", + "Cyan", + "Red", + "Purple", + "Yellow", + "Green", + "Blue", + "Gray" +}; + +const char *french_teams[MAXTEAMS] = { + "Orange", + "Turquoise", + "Rouge", + "Pourpre", + "Jaune", + "Vert", + "Bleu", + "Gris" +}; + +char team_name[MAXTEAMS][40] = { + {"Orange"}, + {"Cyan"}, + {"Red"}, + {"Purple"}, + {"Yellow"}, + {"Green"}, + {"Blue"}, + {"Gray"} +}; + +bool named_team[MAXTEAMS] = { + false, + false, + false, + false, + false, + false, + false, + false +}; + +void set_team_name(Byte team, const char *name) { + if(!name || !name[0]) { + if(config.info.language==1) + strcpy(team_name[team], french_teams[team]); + else + strcpy(team_name[team], english_teams[team]); + named_team[team]=false; + } + else { + strcpy(team_name[team], name); + named_team[team]=true; + } + const char *da_name; + if(named_team[team]) + da_name=team_name[team]; + else + da_name=english_teams[team]; + log_step("team_name\t%s\t%s", log_team(team), da_name); +} + +bool quitting=false; + +void quit_fast() { + quitting=true; + if(sound) + sound->active=false; +} + +const char built[]= +"Built on " \ +__DATE__ \ +" " \ +__TIME__; diff --git a/source/highscores.cpp b/source/highscores.cpp new file mode 100644 index 0000000..8b445eb --- /dev/null +++ b/source/highscores.cpp @@ -0,0 +1,168 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "res_compress.h" +#include "quadra.h" +#include "config.h" +#include "recording.h" +#include "canvas.h" +#include "global.h" +#include "highscores.h" + +int Highscores::numLocal=0; +int Highscores::numGlobal=0; +Highscores::Best Highscores::bestlocal[MAX_SCORE]; +Highscores::Best Highscores::bestglobal[MAX_SCORE]; +bool Highscores::loaded = false; + +void Highscores::getFilename(char* st, int i) { + sprintf(st, "%s/local%i.rec", quadradir, i); +} + +void Highscores::getGlobalFilename(char* st, int i) { + sprintf(st, "%s/global%i.rec", quadradir, i); +} + +void Highscores::load() { + msgbox("Highscores::load\n"); + if(loaded) + return; + loaded=true; + char st[1024], st2[1024]; + int i; + numLocal=0; + for(i=0; iexist) { + Playback* tmp=new Playback(res); + if(!tmp->valid || !tmp->single()) { + delete tmp; + delete res; + continue; + } + strcpy(bestlocal[numLocal].name, tmp->player[0].name); + bestlocal[numLocal].score = tmp->score; + bestlocal[numLocal].level = tmp->level; + bestlocal[numLocal].lines = tmp->lines; + bestlocal[numLocal].demo = tmp; + delete res; // must delete to allow rename (Win95) + if(numLocal!=i) { + getFilename(st2, numLocal); + if(rename(st, st2)) + msgbox("Warning: Highscore: could not rename '%s' to '%s'\n", st, st2); + } + numLocal++; + } + else + delete res; + } + numGlobal=0; + for(i=0; iexist) { + Playback* tmp=new Playback(res); + if(!tmp->valid || !tmp->single()) { + delete tmp; + delete res; + continue; + } + strcpy(bestglobal[numGlobal].name, tmp->player[0].name); + bestglobal[numGlobal].score = tmp->score; + bestglobal[numGlobal].level = tmp->level; + bestglobal[numGlobal].lines = tmp->lines; + bestglobal[numGlobal].demo = tmp; + delete res; // must delete to allow rename (Win95) + if(numGlobal!=i) { + getGlobalFilename(st2, numGlobal); + if(rename(st, st2)) + msgbox("Warning: Highscore: could not rename '%s' to '%s'\n", st, st2); + } + numGlobal++; + } + else + delete res; + } +} + +void Highscores::free() { + int i; + + if(!loaded) + return; + + for(i = 0; i < MAX_SCORE; i++) { + if(bestlocal[i].demo) + delete bestlocal[i].demo; + bestlocal[i].demo = NULL; + if(bestglobal[i].demo) + delete bestglobal[i].demo; + bestglobal[i].demo = NULL; + } + + loaded = false; +} + +int Highscores::update(Canvas *c) { + msgbox("Highscores::update\n"); + char st[1024], st2[1024]; + int i; + load(); + int ret = -1; + for(i=0; istats[CS::SCORE].get_value() >= bestlocal[i].score) { + ret=i; + break; + } + if(numLocalret; j--) { + strcpy(bestlocal[j].name, bestlocal[j-1].name); + bestlocal[j].score = bestlocal[j-1].score; + bestlocal[j].lines = bestlocal[j-1].lines; + bestlocal[j].level = bestlocal[j-1].level; + if(bestlocal[j].demo) + delete bestlocal[j].demo; + bestlocal[j].demo = bestlocal[j-1].demo; + bestlocal[j-1].demo = NULL; + getFilename(st, j); + if(remove(st) != 0) + msgbox("Warning: Highscore: could not delete '%s'\n", st); + getFilename(st2, j-1); + if(rename(st2, st) != 0) + msgbox("Warning: Highscore: could not rename '%s' to '%s'\n", st2, st); + } + sprintf(st2, "%s/last.rec", quadradir); + Playback* demo=NULL; + { + Res_compress res(st2, RES_TRY); + demo=new Playback(&res); + } + getFilename(st, ret); + if(remove(st) == 0) + msgbox("Warning: Highscore: '%s' is in the way! Deleting it.\n", st); + if(rename(st2, st) != 0) + msgbox("Warning: Highscore: could not rename 'last.rec' to '%s'\n", st); + msgbox("Setting hscore %i: %s, %i, %i, %i\n", ret, c->name, c->stats[CS::SCORE].get_value(), c->lines, c->level); + strcpy(bestlocal[ret].name, c->name); + bestlocal[ret].score = c->stats[CS::SCORE].get_value(); + bestlocal[ret].lines = c->lines; + bestlocal[ret].level = c->level; + if(bestlocal[ret].demo) + delete bestlocal[ret].demo; + bestlocal[ret].demo = demo; + } + return ret; +} diff --git a/source/menu.cpp b/source/menu.cpp new file mode 100644 index 0000000..d23b4db --- /dev/null +++ b/source/menu.cpp @@ -0,0 +1,2406 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "utils.h" +#include "raw.h" +#include "pcx.h" +#include "color.h" +#include "random.h" +#include "net.h" +#include "qserv.h" +#include "http_request.h" +#include "cursor.h" +#include "registry.h" +#include "crypt.h" +#include "res_compress.h" +#include "quadra.h" +#include "config.h" +#include "sprite.h" +#include "overmind.h" +#include "inter.h" +#include "input.h" +#include "dict.h" +#include "music.h" +#include "multi_player.h" +#include "misc.h" +#include "zone.h" +#include "net_stuff.h" +#include "net_server.h" +#include "multi_provider.h" +#include "menu_demo_central.h" +#include "chat_text.h" +#include "game_menu.h" +#include "texte.h" +#include "global.h" +#include "game.h" +#include "pane.h" +#include "fonts.h" +#include "highscores.h" +#include "recording.h" +#include "sons.h" +#include "main.h" +#include "canvas.h" +#include "menu.h" + +#ifdef UGS_DIRECTX +#include +#endif +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif + +void Menu_do_nothing::step() { + if(quitting) + ret(); +} + +Menu_highscore::Menu_highscore(int hscore, int *playagain, bool show_playb) { + play_again = playagain; + show_playback = show_playb; + int i; + for(i=0; iset_font(new Font(*fonts.normal, pal, 255,255,255, 0, 20, 40)); + font2 = new Font(*fonts.normal, pal, 255,255,0, 0,20,40); + courrier = new Font(*fonts.courrier, pal, 255,255,255, 0,20,40); + courrier2 = new Font(*fonts.courrier, pal, 255,255,0, 0,20,40); + (void)new Zone_bitmap(inter, bit, 0, 0, true); + + char *pic1, *pic2, *pic1s, *pic2s; + if(config.info.language == 1) { + { + Res_doze res("HSCORETF.RAW"); + Raw raw(res); + Bitmap btemp(raw); + btemp.draw(*bit, 138,0); + } + pic1 = "HSCORE1F.RAW"; + pic2 = "HSCORE2F.RAW"; + pic1s = "HSCOR1FS.RAW"; + pic2s = "HSCOR2FS.RAW"; + } else { + pic1 = "Hscore1.raw"; + pic2 = "Hscore2.raw"; + pic1s = "HSCORE1S.RAW"; + pic2s = "HSCORE2S.RAW"; + } + b_again = b_quit = NULL; + if(show_playback) { + { + Res_doze res1(pic2); + Raw raw1(res1); + Res_doze res2(pic2s); + Raw raw2(res2); + b_quit = new Zone_menu(inter, raw1, 485, 410, raw2); + } + if(play_again) { + Res_doze res1(pic1); + Raw raw1(res1); + Res_doze res2(pic1s); + Raw raw2(res2); + b_again = new Zone_menu(inter, raw1, 38, 410, raw2); + } + } else { + time_demo = 6000; + } + + int x; + if(show_playback) + x = 60; + else + x = 100; + int y = 50; + (void)new Zone_text(inter, ST_HEROES, x, y); + (void)new Zone_text(inter, ST_SCORE, x+190, y); + (void)new Zone_text(inter, ST_LINES, x+290, y); + (void)new Zone_text(inter, ST_LEVEL, x+390, y); + y += 20; + Highscores::load(); + refresh_global(y); //int& + + if(show_playback && net->active) { + sync = new Zone_text_button2(inter, bit, font2, ST_SYNC, x+460, 186); + status = new Zone_text_field(inter, "", 30, 186, 470); + //status = new Zone_text_field(inter, "", 2, 459, 636); + status->set_val(ST_HIGHSTATUSNORMAL); + } else { + sync = NULL; + status = NULL; + } + sync_request = NULL; + + y = 256; + (void)new Zone_text(inter, ST_HEROES, x, y); + (void)new Zone_text(inter, ST_SCORE, x+190, y); + (void)new Zone_text(inter, ST_LINES, x+290, y); + (void)new Zone_text(inter, ST_LEVEL, x+390, y); + y += 20; + for(i=0; iexist) { + y += 6; + (void)new Zone_text(inter, ST_REPLAYLASTGAME, x, y); + playlast = new Zone_text_button2(inter, bit, font2, ST_PLAYBACK, x+460, y); + } + delete res; + } +} + +Menu_highscore::~Menu_highscore() { + delete font2; + delete courrier; + delete courrier2; + if(sync_request) + delete sync_request; +} + +void Menu_highscore::refresh_global(int& y) { + int x; + if(show_playback) + x = 60; + else + x = 100; + zone.deleteall(); + for(int i=0; ineed_paint = 2; +} + +void Menu_highscore::start_sync() { + sync->set_text(ST_CLICKTOCANCEL); + video->need_paint = 2; + Res_dos *demofile; + sync_request = new Qserv(); + if(Highscores::numLocal>=1) { + char st[1024]; + Highscores::getFilename(st, 0); + demofile=new Res_dos(st, RES_TRY); + if(!demofile->exist) { + delete demofile; + demofile=NULL; + } + } + else + demofile=NULL; + if(demofile) { + Playback *demo=Highscores::bestlocal[0].demo; + sync_request->add_data("postdemo\n"); + sync_request->add_data("name %s\n", demo->player); + sync_request->add_data("score %i\n", demo->score); + sync_request->add_data("lines %i\n", demo->lines); + sync_request->add_data("level %i\n", demo->level); + //Add the recording + sync_request->add_data("rec "); + Textbuf buf; + msgbox("Menu_highscore::start_sync: original size=%i\n", demofile->size()); + Http_request::base64encode((const Byte*)demofile->buf(), buf, demofile->size()); + msgbox("Menu_highscore::start_sync: encoded size=%i\n", strlen(buf.get())); + delete demofile; + demofile=NULL; + sync_request->add_data(buf.get()); + sync_request->add_data("\n"); + } + else + sync_request->add_data("gethighscores\n"); + + //Expecting MAX_SCORE global highscores in answer + sync_request->add_data("num %i\n", MAX_SCORE); + sync_request->send(); + status->set_val(ST_HIGHSTATUSCONNECTING); +} + +void Menu_highscore::step_sync() { + bool done = sync_request->done(); + if(!done) { + if(sync_request->isconnected()) { + sprintf(st, ST_HIGHSTATUSRECEIVING, sync_request->getnbrecv()); + status->set_val(st); + } + return; + } + + if(!sync_request->get_status()) { + stop_sync(); + status->set_val(ST_HIGHBADSERVER); + return; + } + if(strcmp(sync_request->get_status(), "Ok")) { + sprintf(st, ST_HIGHSTATUSABORTED, sync_request->get_status()); + status->set_val(st); + stop_sync(); + return; + } + Dict *reply=sync_request->get_reply(); + if(_debug) { + reply->dump(); + } + int i; + for(i=0; ifind_sub(dir); + if(d) { + const char* rec=d->find("rec"); + if(rec) { + char fn[1024]; + Highscores::getGlobalFilename(fn, i); + Res_dos res(fn, RES_CREATE); + if(res.exist) { + Buf out; + msgbox("Menu_highscore::step_sync: encoded size=%i\n", strlen(rec)); + Http_request::base64decode(rec, out, strlen(rec)); + msgbox("Menu_highscore::step_sync: decoded size=%i\n", out.size()); + res.write(out.get(), out.size()); + } + } + } + } + status->set_val(ST_HIGHSTATUSCOMPLETED); + Highscores::free(); + Highscores::load(); + int y=70; + refresh_global(y); + stop_sync(); +} + +void Menu_highscore::stop_sync() { + sync->set_text(ST_SYNC); + video->need_paint = 2; + delete sync_request; + sync_request=NULL; +} + +void Menu_highscore::step() { + Menu_standard::step(); + if(show_playback) { + if(sync_request) + step_sync(); + if(!result) + return; + + if(!sync_request) { + if(play_again) { + if(result == b_again) { + *play_again = 1; + quit = true; + } + } + if(result == b_quit) { + quit = true; + } + if(sync && result == sync) { + start_sync(); + } + for(int i=0; iset_val(ST_HIGHSTATUSCANCELED); + } + } + } else { // mode 'demo' du menu principal + time_demo--; + if(result || input->quel_key != -1 || time_demo == 0) + quit = true; + } +} + +void Menu_highscore::play_demo(const char *st) { + Res_compress *res = new Res_compress(st, RES_TRY); + if(res->exist) { + call(new Fade_in(pal)); + call(new Call_setfont(pal, new Demo_multi_player(res))); + call(new Fade_out(pal)); + // le 'delete res' est fait par ~Demo_multi_player + } else { + msgbox("Menu_highscore::step: Unable to open demo '%s'\n", st); + delete res; + } +} + +Net_starter* net_starter=NULL; + +Net_starter::Net_starter() { + if(net) + (void) new Error("Net already started!\n"); + time_control = TIME_FREEZE; + net=new Net(new Quadra_param()); + if(!net->active) { + msgbox("Net_starter::Net_starter: Can't initialise network."); + char *temp = net->last_error; + if(temp) + msgbox("Error [%s]\n", temp); + else + msgbox("No error reported.\n"); + net_exec = NULL; + } else { + net_exec = new Executor(true); + net_exec->add(new Net_module()); + overmind.start(net_exec); + if(chat_text) + chat_text->addwatch(); + } +} + +Net_starter::~Net_starter() { + if(net_exec) { + overmind.stop(net_exec); + if(chat_text) + chat_text->removewatch(); + } + delete net; + net=NULL; +} + +Menu_multi_join::Menu_multi_join(Bitmap *bit, Font *font, Font *font2, const Palette& p, bool plocal) { + pal = p; + bit_ = bit; + font2_ = font2; + local_net = plocal; + inter->set_font(font, false); + (void)new Zone_bitmap(inter, bit, 0, 0); + (void)new Zone_text(inter, ST_CREATEORJOIN, 20); + b_create = new Zone_text_button2(inter, bit, font2, ST_CLICKTOCREATE, 50); + b_refresh = b_refresh_internet = NULL; + if(local_net) + b_refresh = new Zone_text_button2(inter, bit, font2, ST_CLICKTOREFRESH, 80); + else + b_refresh_internet = new Zone_text_button2(inter, bit, font2, ST_REFRESHINTERNET, 80); + (void)new Zone_text(fteam[7], inter, ST_GAMESLIST, 10, 120); + selected_game = -1; + list_game = new Zone_listbox2(inter, bit, font2, &selected_game, 2, 140, 220, 240); + int y = 120; + (void)new Zone_text(fteam[7], inter, ST_SELECTGAMETYPE, 235, y); + z_game_type = new Zone_text_field(inter, "", 480, y, 155); y += 22; + (void)new Zone_text(fteam[7], inter, ST_GAMESPEED, 235, y); + z_game_speed = new Zone_text_field(inter, "", 480, y, 155); y += 22; + (void)new Zone_text(fteam[7], inter, ST_GAMELEVELSTART, 235, y); + z_game_level = new Zone_text_field(inter, "", 480, y, 24); y += 22; + (void)new Zone_text(fteam[7], inter, ST_GAMEMINIMUM, 235, y); + z_game_minimum = new Zone_text_field(inter, "", 480, y, 155); y += 22; + (void)new Zone_text(fteam[7], inter, ST_SETGAMEEND, 235, y); + z_game_end = new Zone_text_field(inter, "", 480, y, 155); y += 22; + (void)new Zone_text(fteam[7], inter, ST_GAMESTATUS, 235, y); + z_game_status = new Zone_text_field(inter, "", 480, y, 155); y += 22; + //new Zone_text(fteam[7], inter, ST_PLAYERINGAME, 240, 260); + list_player = new Zone_listbox2(inter, bit, font2, NULL, 235, y, 390, 136); + b_join = new Zone_text_button2(inter, bit, font2, ST_CLICKTOJOIN, 395); + + (void)new Zone_text(fteam[7], inter, ST_ENTERADDRESS, 30, 420); + address[0] = 0; + (void)new Zone_input_address(this, pal, inter, address, 254, 340, 420, 240); + b_info = new Zone_text_button2(inter, bit, font2, ST_CHECKIP, 35, 450); + b_book = NULL; + if(!local_net) + b_book = new Zone_text_button2(inter, bit, font2, ST_ADDRESSBOOK, 345, 450); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); +} + +Menu_multi_join::~Menu_multi_join() { + removewatch(); +} + +void Menu_multi_join::addwatch() { + net->addwatch(P_GAMEINFO, this); +} + +void Menu_multi_join::removewatch() { + net->removewatch(P_GAMEINFO, this); +} + +void Menu_multi_join::init() { + Menu::init(); + if(!net->active) { + exec(new Menu_net_problem(net->last_error, ST_NETWORKDOWN, bit_, inter->font)); + } else { + net->close_all_udp(); + net->init_all_udp(); + refresh(); + } +} + +class Listgame: public Listable { +public: + Packet_gameinfo *p; + Listgame(const char *s, Packet_gameinfo *pp): Listable(s) { + p = pp; + } + virtual ~Listgame() { + delete p; + } + virtual bool is_equal(Listable *source) { + Listgame *other=(Listgame *) source; + return p->from_addr == other->p->from_addr && + p->port == other->p->port; + } +}; + +void Menu_multi_join::step() { + Menu::step(); + if(input->quel_key == 1 || quitting) { + ret(); + return; + } + + if(!result) + return; + + if(result==b_create) { + removewatch(); + if(address[0] == 0 && local_net) + refresh(); + call(new Create_game(bit_, inter->font, font2_, pal, true, local_net)); + } + if(result==b_refresh || result==b_refresh_internet) { + address[0] = 0; // ignore l'adresse IP ecrite dans la boite + refresh(); + } + if(result==b_info) { + call(new Menu_multi_checkip(bit_, inter->font, font2_, pal)); + } + if(result==b_book) { + call(new Menu_multi_book(bit_, inter->font, font2_, pal, NULL)); + } + Listgame *lg = (Listgame *) list_game->get_selected(); + if(lg && list_game->in_listbox(result)) + refresh_player(); + if(lg && (list_game->in_listbox(inter->double_clicked) || result==b_join)) { + Packet_gameinfo *p = lg->p; + if(p->version==20 || p->version==22 || p->version==Config::net_version) { + if(address[0] == 0 && local_net) + refresh(); + join_game(p->name, p->from_addr, p->port); + } + } + if(result == cancel) { + ret(); + return; + } +} + +void Menu_multi_join::join_game(char *nam, Dword adr, int port) { + removewatch(); + call(new Join_game(bit_, inter->font, font2_, pal, nam, adr, port, false)); +} + +void Menu_multi_join::refresh() { + call(new Menu_multi_refresh(this)); +} + +void Menu_multi_join::clear_game_info() { + list_player->clear(); + z_game_type->set_val(""); + z_game_speed->set_val(""); + z_game_level->set_val(""); + z_game_minimum->set_val(""); + z_game_end->set_val(""); + z_game_status->set_val(""); +} + +void Menu_multi_join::refresh_player() { + clear_game_info(); + Listgame *lg = (Listgame *) list_game->get_selected(); + Packet_gameinfo *p = lg->p; + + for(int i=0; iplayers.size(); i++) { + char name[256]; + strcpy(name, p->players[i]->name); + if(p->players[i]->idle==3) + strcat(name, " *"); + list_player->add_item(new Listable(name, fteam[p->players[i]->team])); + } + + if(p->version >= 20) { + const char *gtype=ST_GAMETYPE1; + if(p->survivor) + gtype=ST_GAMETYPE2; + if(p->normal_attack.type==ATTACK_NONE && p->clean_attack.type==ATTACK_NONE) + gtype=ST_GAMETYPE4; + if(p->normal_attack.type==ATTACK_BLIND || p->normal_attack.type==ATTACK_FULLBLIND) + gtype=ST_GAMETYPE5; + if(p->hot_potato) + gtype=ST_GAMETYPE3; + z_game_type->set_val(gtype); + if(!p->nolevel_up) + z_game_speed->set_val(ST_GAMELEVELUP1); + else + z_game_speed->set_val(ST_GAMELEVELUP2); + game_level_start = (int) p->level_start; + z_game_level->set_val(&game_level_start); + z_game_minimum->set_val(ST_BASE(110 + p->combo_min)); + switch(p->game_end) { + case 0: z_game_end->set_val(ST_GAMEEND1); break; + case 1: + sprintf(game_end_text, "%i %s", p->game_end_value, ST_GAMEENDFRAG); + z_game_end->set_val(game_end_text); + break; + case 2: + sprintf(game_end_text, "%i %s", p->game_end_value / 6000, ST_GAMEENDMINUTE); + z_game_end->set_val(game_end_text); + break; + case 3: + sprintf(game_end_text, "%i %s", p->game_end_value, ST_GAMEENDSCORE); + z_game_end->set_val(game_end_text); + break; + case 4: + sprintf(game_end_text, "%i %s", p->game_end_value, ST_GAMEENDLINES); + z_game_end->set_val(game_end_text); + break; + } + if(p->terminated) { + z_game_status->set_val(ST_GAMESTATUSTERM); + } else { + if(p->delay_start) + z_game_status->set_val(ST_GAMESTATUSNOT); + else + z_game_status->set_val(ST_GAMESTATUSPLAY); + } + } + + if(p->version < 20/*Config::net_version*/) { + z_game_type->set_val(ST_OLDERVERSION); + } + if(p->version > Config::net_version) { + z_game_type->set_val(ST_NEWERVERSION); + } +} + +void Menu_multi_join::net_call(Packet *p2) { + Packet_gameinfo *p=(Packet_gameinfo *)p2; +// char ad[256]; +// Net::stringaddress(ad, p->from_addr); + + if(p->name[0]) + sprintf(st, "%s", p->name); + else + sprintf(st, "%s", ST_GAMENONAME); + Listgame *lg = new Listgame(st, p); // le packet 'p' sera deleter par ~Listgame() + + int deja = list_game->search(lg); + if(deja != -1) { + list_game->replace_item(deja, lg); + if(selected_game != -1) + if(list_game->get_selected() == lg) + refresh_player(); + } else { + list_game->add_item(lg); + } +} + +void Zone_input_address::lost_focus(int cancel) { + Zone_text_input::lost_focus(cancel); + if(!cancel && val[0] != 0) { + parent->refresh(); + } +} + +Menu_multi_refresh::Menu_multi_refresh(Menu_multi_join *p): Menu(p->inter) { + parent = p; +} + +void Menu_multi_refresh::init() { + Menu::init(); + parent->list_game->clear(); + parent->clear_game_info(); + + if(parent->address[0] != 0) { + resolve(); + return; + } + if(parent->local_net) + find_local_games(); + else + find_internet_games(); +} + +void Menu_multi_refresh::find_local_games() { + Packet_findgame p; + Dword to; + parent->addwatch(); + to = INADDR_BROADCAST; + net->sendudp(to, &p); + char *error = net->failed(); + if(error) { + call(new Menu_net_problem(error, ST_SENDUDPFAILED, parent->bit_, inter->font)); + } + ret(); +} + +void Menu_multi_refresh::resolve() { + Dword to = net->getaddress(parent->address); + if(to > 0) { + ret(); + parent->join_game(NULL, to, net->port_resolve); + } else { + cancel = NULL; + call(new Wait_time(50)); // attend 1/2 seconde (au cas ou le host se resolve tres vite) + } +} + +void Menu_multi_refresh::find_internet_games() { + exec(new Menu_multi_internet(parent)); +} + +void Menu_multi_refresh::step() { + Menu::step(); + if(!cancel) { + (void)new Zone_bitmap(inter, parent->bit_, 0, 0); + (void)new Zone_text(fteam[7], inter, ST_LOOKINGFORHOST, 130); + cancel = new Zone_text_button2(inter, parent->bit_, parent->font2_, ST_CLICKTOCANCEL, 190); + } + if(net->name_resolve != (unsigned int)-1) { + Dword name_temp = net->name_resolve; + net->name_resolve = (Dword)-1; + if(name_temp == 0) { + (void)new Zone_text(fteam[7], inter, ST_ERRORLOOKING, 220); + } else { + int port = net->port_resolve; + if(!port) + port = config.info.port_number; // valeur par defaut + parent->join_game(NULL, name_temp, port); + ret(); + return; + } + } + if(result == cancel || input->quel_key == 1) { + net->gethostbyname_cancel(); + input->quel_key = 0; + ret(); + } +} + +Menu_multi_internet::Menu_multi_internet(Menu_multi_join *p): Menu(p->inter) { + parent = p; + new Zone_bitmap(inter, parent->bit_, 0, 0); + new Zone_text(fteam[7], inter, ST_RECEIVINGINTERNET, 130); + cancel = new Zone_text_button2(inter, parent->bit_, parent->font2_, ST_CLICKTOCANCEL, 190); + request = NULL; +} + +Menu_multi_internet::~Menu_multi_internet() { + if(request) + delete request; +} + +void Menu_multi_internet::init() { + Menu::init(); + char *msg; + + request = new Qserv(); + request->add_data("getgames\n"); + request->send(); + msg = net->failed(); + if(msg) { + exec(new Menu_net_problem(msg, ST_NOSERVERFOUND2, parent->bit_, inter->font)); + delete request; + request = NULL; + } +} + +void Menu_multi_internet::step() { + Menu::step(); + if(!request) + return; + bool done = request->done(); + if(net->failed()) { + exec(new Menu_net_problem(ST_NOSERVERFOUND, ST_NOSERVERFOUND2, parent->bit_, inter->font)); + return; + } + if(done) + parsegames(); + if(result == cancel || input->quel_key == 1) { + input->quel_key = 0; + ret(); + } +} + +Attack Menu_multi_internet::parse_attack(const char *s) { + Attack ret; + if(s) { + ret.type = (Attack_type) atoi(s); + const char *sep=strchr(s, ' '); + if(sep) + ret.param=atoi(sep+1); + } + return ret; +} + +void Menu_multi_internet::parsegames() { + ret(); + if(!request->get_status() || strcmp(request->get_status(), "Current games")) + return; + + Dict *games=request->get_reply(); + if(_debug) { + games->dump(); + } + unsigned int i, j; + for(i = 0; i < games->size(); i++) { + const Dict *d = games->get_sub(i); + Packet_gameinfo *p=new Packet_gameinfo(); + + p->from = NULL; +// Word port=0; + char cip[32]; + strcpy(cip, d->get_key()); + char *cport=strchr(cip, ':'); + if(cport) { +// port=atoi(cport+1); + *cport=0; //Cut string at ':' separator + } + p->from_addr = Net::dotted2addr(cip); + const char *temp; + temp = d->find("name"); + if(temp) + strcpy(p->name, temp); + temp = d->find("version"); + if(temp) + p->version = atoi(temp); + temp = d->find("port"); + if(temp) + p->port = atoi(temp); + + Dict *players = d->find_sub("players"); + if(players) { + for(j=0; j < players->size(); j++) { + const Dict *d2 = players->get_sub(j); + const char *name = d2->find("name"); + temp = d2->find("team"); + int team=0; + if(temp) { + team = atoi(temp); + if(team<0 || team>7) + team=0; + } + int status=-1; + temp = d2->find("status"); + if(temp) { + status = atoi(temp); + if(status<0 || status>3) + status=-1; + } + int handicap=0; + temp = d2->find("handicap"); + if(temp) { + handicap = atoi(temp); + if(handicap<0 || handicap>4) + handicap=0; + } + p->add_player(0, team, name, status, handicap); + } + } + + Dict *end = d->find_sub("end"); + if(end) { + temp = end->find("value"); + if(temp) + p->game_end_value = atoi(temp); + temp = end->find("type"); + if(temp) + p->game_end = atoi(temp); + } + + Dict *rules = d->find_sub("rules"); + if(rules) { + temp = rules->find("levelup"); + if(temp) + p->nolevel_up = atoi(temp) ? false:true; + temp = rules->find("levelstart"); + if(temp) + p->level_start = atoi(temp); + temp = rules->find("mincombo"); + if(temp) + p->combo_min = atoi(temp); + temp = rules->find("allowhandicap"); + if(temp) + p->allow_handicap = atoi(temp)? true:false; + temp = rules->find("survivor"); + if(temp) + p->survivor = atoi(temp)? true:false; + temp = rules->find("hot_potato"); + if(temp) + p->hot_potato = atoi(temp)? true:false; + Dict *attacks = rules->find_sub("attacks"); + if(attacks) { + temp = attacks->find("normal"); + p->normal_attack = parse_attack(temp); + temp = attacks->find("clean"); + p->clean_attack = parse_attack(temp); + temp = attacks->find("potato_normal"); + p->potato_normal_attack = parse_attack(temp); + temp = attacks->find("potato_clean"); + p->potato_clean_attack = parse_attack(temp); + } + } + + Dict *status = d->find_sub("status"); + if(status) { + temp = status->find("started"); + if(temp) + p->delay_start = atoi(temp) ? false:true; + temp = status->find("terminated"); + if(temp) + p->terminated = atoi(temp) ? true:false; + } + + parent->net_call(p); + } +} + +Menu_single::Menu_single() { + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + + pal.set_size(256); //Useless but whatever + set_fteam_color(pal); + + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + font2 = new Font(*fonts.normal, pal, 255,255,0); + (void)new Zone_bitmap(inter, bit, 0, 0, true); + (void)new Zone_text(inter, ST_SELECTSINGLEGAMETYPE, 20); + normal = new Zone_text_button2(inter, bit, font2, ST_SINGLENORMAL, 180); + sprint = new Zone_text_button2(inter, bit, font2, ST_SINGLESPRINT, 220); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); +} + +void Menu_single::step() { + Menu_standard::step(); + if(result == normal) { + call(new Fade_in(pal)); + call(new Single_player(PRESET_SINGLE)); + call(new Fade_out(pal)); + } + if(result == sprint) { + call(new Fade_in(pal)); + call(new Single_player(PRESET_SINGLE_SPRINT)); + call(new Fade_out(pal)); + } + if(result == cancel) + quit=true; +} + +Menu_single::~Menu_single() { + delete font2; +} + +Menu_multi::Menu_multi() { + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + + pal.set_size(256); //Useless but whatever + set_fteam_color(pal); + + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + font2 = new Font(*fonts.normal, pal, 255,255,0); + (void)new Zone_bitmap(inter, bit, 0, 0, true); + (void)new Zone_text(inter, ST_SELECTCONNECTION, 20); + local = new Zone_text_button2(inter, bit, font2, ST_LOCALGAME, 180); + net_lan = new Zone_text_button2(inter, bit, font2, ST_LOCALNETWORK, 220); + net_internet = new Zone_text_button2(inter, bit, font2, ST_INTERNET, 260); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); +} + +void Menu_multi::step() { + Menu_standard::step(); + if(result == local) { + call(new Create_game(bit, inter->font, font2, pal, false, false)); + } + if(result == net_lan) { + call(new Menu_multi_join(bit, inter->font, font2, pal, true)); + } + if(result == net_internet) { + call(new Menu_multi_join(bit, inter->font, font2, pal, false)); + } + if(result == cancel) + quit=true; +} + +Menu_multi::~Menu_multi() { + delete font2; +} + +Menu_setup::Menu_setup() { + { + char *tpic; + if(config.info.language == 0) { + tpic = "SETUP.RAW"; + } else { + tpic = "SETUPF.RAW"; + } + Res_doze res(tpic); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + set_fteam_color(pal); + (void)new Zone_bitmap(inter, bit, 0, 0, true); + + Zone_state_text2 *z_setup_player = new Zone_state_text2(inter, &config.info.setup_player, 333, 11); + z_setup_player->add_string("1"); + z_setup_player->add_string("2"); + z_setup_player->add_string("3"); + + z_nameinput = new Zone_text_input(inter, pal, config.player[config.info.setup_player].name, 40, 333, 106, 110, 100); + + z_passwdinput = new Zone_text_input(inter, pal, config.player2[config.info.setup_player].ngPasswd, 64, 333, 132, 200); + + z_shadow = new Zone_state_text2(inter, &config.player[config.info.setup_player].shadow, 333, 161); + z_shadow->add_string(ST_NO); + z_shadow->add_string(ST_YES); + + z_smooth = new Zone_state_text2(inter, &config.player[config.info.setup_player].smooth, 333, 192); + z_smooth->add_string(ST_NO); + z_smooth->add_string(ST_YES); + + z_h_repeat = new Zone_state_text(inter, &config.player2[config.info.setup_player].h_repeat, 333, 220); + z_h_repeat->add_string(ST_SLOW); + z_h_repeat->add_string(ST_NORMAL); + z_h_repeat->add_string(ST_FAST); + z_h_repeat->add_string(ST_FASTER); + + z_v_repeat = new Zone_state_text(inter, &config.player2[config.info.setup_player].v_repeat, 333, 252); + z_v_repeat->add_string(ST_SLOW); + z_v_repeat->add_string(ST_NORMAL); + z_v_repeat->add_string(ST_FAST); + z_v_repeat->add_string(ST_FASTER); + + z_continuousdown = new Zone_state_text(inter, &config.player2[config.info.setup_player].continuous, 333, 281); + z_continuousdown->add_string(ST_NO); + z_continuousdown->add_string(ST_YES); + + z_key[0] = new Zone_set_key(inter, &config.player[config.info.setup_player].key[0], 148, 347); + z_key[1] = new Zone_set_key(inter, &config.player[config.info.setup_player].key[1], 148, 378); + z_key[3] = new Zone_set_key(inter, &config.player[config.info.setup_player].key[3], 148, 408); + z_key[6] = new Zone_set_key(inter, &config.player2[config.info.setup_player].key[1], 148, 439); + z_key[4] = new Zone_set_key(inter, &config.player[config.info.setup_player].key[4], 513, 350); + z_key[2] = new Zone_set_key(inter, &config.player[config.info.setup_player].key[2], 513, 380); + z_key[5] = new Zone_set_key(inter, &config.player2[config.info.setup_player].key[0], 513, 411); + + if(config.info.language == 0) { + b_player = new Zone_menu(inter, bit, "SETUP0.RAW", 195, 11); + b_player->set_child(z_setup_player); + (new Zone_menu(inter, bit, "SETUP1.RAW", 255, 101))->set_child(z_nameinput); + (new Zone_menu(inter, bit, "SETUP2.RAW", 222, 132))->set_child(z_passwdinput); + (new Zone_menu(inter, bit, "SETUP3.RAW", 237, 161))->set_child(z_shadow); + (new Zone_menu(inter, bit, "SETUP4.RAW", 240, 192))->set_child(z_smooth); + (new Zone_menu(inter, bit, "SETUP5.RAW", 157, 220))->set_child(z_h_repeat); + (new Zone_menu(inter, bit, "SETUP6.RAW", 184, 252))->set_child(z_v_repeat); + (new Zone_menu(inter, bit, "SETUP7.RAW", 153, 281))->set_child(z_continuousdown); + b_key[0] = new Zone_menu(inter, bit, "SETUP8.RAW", 45, 347); + b_key[1] = new Zone_menu(inter, bit, "SETUP9.RAW", 32, 378); + b_key[3] = new Zone_menu(inter, bit, "SETUP10.RAW", 26, 408); + b_key[6] = new Zone_menu(inter, bit, "SETUP11.RAW", 19, 439); + b_key[4] = new Zone_menu(inter, bit, "SETUP12.RAW", 344, 350); + b_key[2] = new Zone_menu(inter, bit, "SETUP13.RAW", 275, 380); + b_key[5] = new Zone_menu(inter, bit, "SETUP14.RAW", 382, 411); + b_all_key = new Zone_menu(inter, bit, "SETUP15.RAW", 390, 439); + } else { + b_player = new Zone_menu(inter, bit, "SETUPF0.RAW", 137, 11); + b_player->set_child(z_setup_player); + (new Zone_menu(inter, bit, "SETUPF1.RAW", 266, 101))->set_child(z_nameinput); + (new Zone_menu(inter, bit, "SETUPF2.RAW", 193, 132))->set_child(z_passwdinput); + (new Zone_menu(inter, bit, "SETUPF3.RAW", 244, 162))->set_child(z_shadow); + (new Zone_menu(inter, bit, "SETUPF4.RAW", 265, 192))->set_child(z_smooth); + (new Zone_menu(inter, bit, "SETUPF5.RAW", 140, 221))->set_child(z_h_repeat); + (new Zone_menu(inter, bit, "SETUPF6.RAW", 166, 252))->set_child(z_v_repeat); + (new Zone_menu(inter, bit, "SETUPF7.RAW", 127, 281))->set_child(z_continuousdown); + b_key[0] = new Zone_menu(inter, bit, "SETUPF8.RAW", 60, 349); + b_key[1] = new Zone_menu(inter, bit, "SETUPF9.RAW", 71, 380); + b_key[3] = new Zone_menu(inter, bit, "SETUPF10.RAW", 95, 409); + b_key[6] = new Zone_menu(inter, bit, "SETUPF11.RAW", 64, 438); + b_key[4] = new Zone_menu(inter, bit, "SETUPF12.RAW", 354, 350); + b_key[2] = new Zone_menu(inter, bit, "SETUPF13.RAW", 316, 381); + b_key[5] = new Zone_menu(inter, bit, "SETUPF14.RAW", 358, 410); + b_all_key = new Zone_menu(inter, bit, "SETUPF15.RAW", 359, 440); + } + + for(int i=0; i<7; i++) + b_key[i]->set_child(z_key[i]); + + b_quit = new Zone_text_button2(inter, bit, fteam[4], ST_BACK, 560, 450); +} + +Menu_setup::~Menu_setup() { + int i; + for(i=0; i<3; i++) + if(!config.player[i].name[0]) { + char st[16]; + sprintf(st,"#%i", i+1); + strcpy(config.player[i].name, st); + } + config.write(); +} + +void Menu_setup::step() { + Menu_standard::step(); + if(!result) { + return; + } + + if(result == b_quit) { + quit = true; + } + if(result == b_player) { + z_nameinput->set_val(config.player[config.info.setup_player].name); + z_passwdinput->set_val(config.player2[config.info.setup_player].ngPasswd); + z_shadow->set_val(&config.player[config.info.setup_player].shadow); + z_smooth->set_val(&config.player[config.info.setup_player].smooth); + z_h_repeat->set_val(&config.player2[config.info.setup_player].h_repeat); + z_v_repeat->set_val(&config.player2[config.info.setup_player].v_repeat); + z_continuousdown->set_val(&config.player2[config.info.setup_player].continuous); + for(int i=0; i<7; i++) + if(i<5) + z_key[i]->set_val(&config.player[config.info.setup_player].key[i]); + else + z_key[i]->set_val(&config.player2[config.info.setup_player].key[i-5]); + } + if(result == b_all_key) { + call(new Menu_setup_all_key(inter, z_key)); + } + for(int i=0; i<7; i++) { + if(result==b_key[i]) { + call(new Menu_setup_key(inter, (Zone_set_key *) z_key[i], ST_PRESSAKEY)); + } + } +} + +Menu_setup_all_key::Menu_setup_all_key(Inter *in, Zone_set_key *k[]): Menu(in) { + key = k; + quel=0; +} + +void Menu_setup_all_key::step() { + static char touche[7] = {0, 1, 3, 4, 2, 5, 6}; + Menu::step(); + if(quel > 6 || input->keys[1] & PRESSED) { + ret(); + return; + } + const char *te = NULL; + switch(touche[quel]) { + case 0: te = ST_PRESSLEFT; break; + case 1: te = ST_PRESSRIGHT; break; + case 2: te = ST_PRESSROTCCW; break; + case 3: te = ST_PRESSDOWN; break; + case 4: te = ST_PRESSROTCW; break; + case 5: te = ST_PRESS2ROT; break; + case 6: te = ST_PRESSDROP; break; + } + call(new Menu_setup_key(inter, key[touche[quel]], te)); + quel++; +} + +Menu_setup_key::Menu_setup_key(Inter *in, Zone_set_key *k, const char *t): Menu(in) { + new Zone_text_field(inter, t, 180, 315, 280); + key = k; +} + +Menu_setup_key::Menu_setup_key(Inter *in, Zone_set_key *k): Menu(in) { + key = k; +} + +void Menu_setup_key::init() { + input->clear_key(); +} + +void Menu_setup_key::step() { + Menu::step(); + int i,loop=0; + for(i=1; i<256; i++) { + // refuse la touche 'ENTER' (car dedie au Chat!) + if(i == KEY_ENTER) + continue; + if(input->keys[i] & PRESSED) { + loop=i; + break; + } + } + if(loop > 0) { + if(loop > 1) { + *(key->val) = loop; + key->process(); + } + input->quel_key = -1; + ret(); + } +} + +Menu_quitgame::Menu_quitgame() { + int y; + for(y=0; y<8; y++) + pal.setcolor(y, y*255/7, y*255/7, y*255/7); + pal.setcolor(255, 255, 255, 255); + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + for(y=8; y<16; y++) + pal.setcolor(y, 0, 0, y*255/7); + font2 = new Font(*fonts.normal, pal, 0,0,255); +} + +Menu_quitgame::~Menu_quitgame() { + delete font2; +} + +void Menu_quitgame::init() { + Menu_standard::init(); + (void)new Zone_clear(inter); + (void)new Zone_text(inter, ST_QUADRA, 0); + (void)new Zone_text(inter, ST_DEMOQUIT1, 10, 40); + int t,h,m,s; + t = overmind.framecount/100; + h = t/60/60; t=t%3600; + m = t/60; + s = t%60; + if(h > 0) { + sprintf(st,ST_DEMOQUIT2_1,h,m); + } else if(m > 0) { + sprintf(st,ST_DEMOQUIT2_2,m,s); + } else { + sprintf(st,ST_DEMOQUIT2_3,s); + } + (void)new Zone_text(inter, st, 10, 60); + (void)new Zone_text(inter, ST_DEMOQUIT3, 10,80); + (void)new Zone_text(inter, ST_DEMOQUIT4, 10,100); + (void)new Zone_text(inter, ST_DEMOQUIT5, 10,120); + (void)new Zone_text(font2, inter, ST_DEMOQUIT6, 10, 150); + //new Zone_text(inter, ST_DEMOQUIT7, 10, 180); + //new Zone_text(inter, ST_DEMOQUIT8, 10, 200); + //new Zone_text(inter, ST_DEMOQUIT9, 10, 220); + //new Zone_text(inter, ST_DEMOQUIT10, 10, 240); + (void)new Zone_text(inter, ST_DEMOQUIT11, 10, 260); + (void)new Zone_text(inter, ST_DEMOQUIT12, 10, 280); + (void)new Zone_text(inter, ST_DEMOQUIT13, 10, 300); + (void)new Zone_text(inter, ST_DEMOQUIT14, 10, 320); + #ifdef UGS_DIRECTX + sprintf(st,ST_DEMOQUIT15,"Linux"); + #else + sprintf(st,ST_DEMOQUIT15,"Windows"); + #endif + (void)new Zone_text(inter, st, 10, 360); + (void)new Zone_text(inter, ST_DEMOQUIT16, 10, 380); + (void)new Zone_text(font2, inter, ST_DEMOQUIT17, 20, 410); +} + +void Menu_quitgame::step() { + Menu_standard::step(); + if(!quit) + call(new Wait_time(24000)); + quit=true; +} + +Menu_help::Menu_help() { + Bitmap *bit; + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + pal.set_size(256); + set_fteam_color(pal); + + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + font2 = fteam[4]; + (void)new Zone_bitmap(inter, bit, 0, 0, true); +// new Zone_clear(inter); + b_quit = new Zone_text_button2(inter, NULL, font2, ST_BACK, 560, 450); + int y; + if(config.registered) { + (void)new Zone_text(inter, ST_HELP1, 20); + (void)new Zone_text(fteam[5], inter, ST_HELP2, 60); + (void)new Zone_text(fteam[5], inter, ST_HELP3, 100); + (void)new Zone_text(inter, ST_HELP4, 20, 140); + (void)new Zone_text_field(inter, Config::user_name, 320, 140, 310, fteam[1]); + y = 220; + b_online=b_register=NULL; + } else { + (void)new Zone_text(fteam[5], inter, ST_REGISTER1, 20); + (void)new Zone_text(inter, ST_REGISTER2, 80); + (void)new Zone_text(inter, ST_REGISTER3, 100); + (void)new Zone_text(inter, ST_REGISTER4, 120); + (void)new Zone_text(inter, ST_REGISTER5, 140); + Zone_text *temp; + b_online = temp = new Zone_text_select(inter, fteam[4], ST_REGISTER9, 160, 180); // on-line register + temp->set_font(fteam[6]); + b_register = new Zone_text_button2(inter, NULL, font2, ST_CLICKREGISTER, 220, 220); + y = 260; + } + + (void)new Zone_text(inter, ST_HELP10, 10, y); y+=20; + (void)new Zone_text(inter, ST_HELP11, 10, y); + Zone_text *temp; + b_email = temp = new Zone_text_select(inter, fteam[4], ST_HELP12, 160, y); // email ludus + temp->set_font(fteam[6]); y+=40; + (void)new Zone_text(fteam[3], inter, ST_HELP15, y); y+=20; + (void)new Zone_text(inter, ST_HELP16, 10, y); y+=20; + (void)new Zone_text(inter, ST_HELP17, 10, y); y+=20; + (void)new Zone_text(inter, ST_HELP18, 10, y); y+=20; + (void)new Zone_text(inter, ST_CREDIT1, 10, y); y+=20; + (void)new Zone_text(inter, ST_CREDIT2, 10, y); y+=20; + (void)new Zone_text(inter, ST_HELP19, 10, y); + b_www = temp = new Zone_text_select(inter, fteam[4], ST_HELP20, 160, y); // www address + temp->set_font(fteam[6]); +} + +void Menu_help::init() { + Menu_standard::init(); + if(config.registered) { + Sfx stmp(sons.levelup, 0, 0, 0, 11000); + } +} + +void Menu_help::step() { + Menu_standard::step(); + if(!result) + return; + if(result == b_quit) + quit = true; +#ifdef UGS_DIRECTX + if(result == b_www) + call_internet(ST_HELP20); + if(result == b_email) + call_internet("mailto:support@ludusdesign.com?subject=Quadra"); + if(result == b_online) + call_internet(ST_REGISTER9); +#endif + if(result == b_register) { + exec(new Menu_register(inter, pal)); + //call(new Fade_out(pal)); + } +} + +void Menu_help::call_internet(const char *s) { + call(new Fade_in(pal)); + call(new Menu_internet(s)); + call(new Fade_to(Palette(), pal)); +} + +Menu_register::Menu_register(Inter *in, const Palette &p): Menu_quit(in) { + Bitmap *bit; + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + } + pal=p; + set_fteam_color(pal); + Font *font2 = fteam[4]; + new Zone_bitmap(inter, bit, 0, 0, true); + //new Zone_clear(inter); + b_quit = new Zone_text_button2(inter, NULL, font2, ST_BACK, 560, 450); + new Zone_text(fteam[5], inter, ST_REGISTER1, 20); + new Zone_text(inter, ST_REGISTER10, 80); + name[0] = 0; + pass[0] = 0; + new Zone_text(inter, ST_NAME, 10, 120); + new Zone_text_input(inter, pal, name, 63, 140, 120, 360); + new Zone_text(inter, ST_PASSWORD, 10, 160); + z_pass = new Zone_text_input(inter, pal, pass, 33, 140, 160, 360); + b_ok = new Zone_text_button2(inter, NULL, font2, ST_CLICKREGISTER, 220, 230); + z_invalid = NULL; +} + +void Menu_register::step() { + Menu_quit::step(); + if(z_invalid) { + delete z_invalid; + z_invalid = NULL; + video->need_paint = 2; + } + if(!result) + return; + if(result == b_quit) + quit = true; + if(result == b_ok) { + bool ok=false; + if(strlen(name) > 0 && strlen(pass) > 0) { + Crypt cr(name, true); + #ifdef _DEBUG // Pour pas pouvoir obtenir une clé avec -debug + msgbox("Menu_register::step: \n name=%s\n crypt=%s\n pass=%s\n", name, cr.get_digest_string(), pass); + #endif + if(stricmp(cr.get_digest_string(), pass) == 0) + ok=true; + } + if(ok) { + config.registered = true; + strncpy(Config::user_name, name, 63); + Config::user_name[63] = 0; + + Registry *r = Registry::alloc(); + r->open("Quadra", quadradir); + r->write("User name", name); + r->write("Password", pass); + r->close(); + delete r; + quit = true; + exec(new Menu_help()); + call(new Fade_out(pal)); + } else { + pass[0] = 0; + z_pass->set_val(pass); + video->need_paint = 2; + z_invalid = new Zone_text(inter, ST_PASSWORDINVALID, 280); + call(new Wait_time(300, true)); + } + } +} + +Menu_option::Menu_option() { + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + pal.set_size(256); + set_fteam_color(pal); + + Palette ptemp; + { + Res_doze res("Fond0.pcx"); + Pcx pcx(res); + ptemp.load(pcx); + } + for(int i=184; i<256; i++) // copie les couleurs des blocs de l'image fond0.pcx + pal.setcolor(i, ptemp.r(i), ptemp.g(i), ptemp.b(i)); + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + set_fteam_color(pal); + font2 = fteam[4]; +// font2 = new Font(*fonts.normal, pal, 255,255,0); + (void)new Zone_bitmap(inter, bit, 0, 0, true); + (void)new Zone_text(inter, ST_GAMEOPTION, 20); + b_quit = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); + + (void)new Zone_text(fteam[7], inter, ST_SELECTLANGUAGE, 40, 100); + { + Zone_state_text2 *temp = new Zone_state_text2(inter, &config.info.language, 380, 100); + temp->add_string(ST_ENGLISH); + temp->add_string(ST_FRENCH); + } + old_language = config.info.language; + + (void)new Zone_text(fteam[7], inter, ST_SELECTCDMUSIC, 40, 130); + { + Zone_state_text2 *temp = new Zone_state_text2(inter, &config.info.cdmusic, 380, 130); + temp->add_string(ST_CDMUSIC1); + temp->add_string(ST_CDMUSIC2); + temp->add_string(ST_CDMUSIC3); + } + old_music = config.info.cdmusic; + + (void)new Zone_text(fteam[7], inter, ST_SETMOUSESPEED, 40, 160); + (void)new Zone_input_numeric(inter, &config.info.mouse_speed, 4, 1, 255, pal, 380, 160, 60); + old_mouse_speed = config.info.mouse_speed; + old_port = config.info.port_number; + + (void)new Zone_text(inter, ST_ADVANCEDOPTION, 40, 220); + + (void)new Zone_text(fteam[7], inter, ST_ENTERINTERNETPORT, 40, 250); + (void)new Zone_input_numeric(inter, &config.info.port_number, 6, 1024, 65535, pal, 380, 250, 60); + (void)new Zone_text(fteam[3], inter, ST_NOTEPORTNUMBER, 40, 274); + + (void)new Zone_text(fteam[7], inter, ST_SETGAMESERVER, 40, 310); + (void)new Zone_text_input(inter, pal, config.info.game_server_address, 255, 380, 310, 240); + (void)new Zone_text(fteam[3], inter, ST_DEFAULTGAMESERVER, 40, 334); + strcpy(old_server, config.info.game_server_address); +} + +Menu_option::~Menu_option() { + config.write(); + if(old_music != config.info.cdmusic) { + if(config.info.cdmusic == 2) { + music->play(1, true); + return; + } + if(old_music == 0) { + music->play(1); + } + if(config.info.cdmusic == 0) { + music->stop(); + } + } + if(old_language != config.info.language) { + delete stringtable; + char *language; + switch(config.info.language) { + default: + case 0: + language="anglais.txt"; break; + case 1: + language="francais.txt"; break; + } + stringtable=new Stringtable(language); + int i; + for(i=0; iset_speed(config.info.mouse_speed); + old_mouse_speed = config.info.mouse_speed; + } +} + +Menu_intro::Menu_intro() { + int y; + for(y=0; y<8; y++) + pal.setcolor(y, y*255/7, y*255/7, y*255/7); + for(y=8; y<16; y++) + pal.setcolor(y, 0, y*255/7, y*255/7); + pal.setcolor(255, 255, 255, 255); + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + font2 = new Font(*fonts.normal, pal, 0,255,255); + (void)new Zone_clear(inter); + (void)new Zone_text(inter, ST_INTRO1, 10); + (void)new Zone_text(inter, ST_INTRO2, 10, 40); + if(!config.registered) + (void)new Zone_text(font2, inter, ST_INTRO3, 10, 80); + else + (void)new Zone_text(font2, inter, ST_INTRO4, 10, 80); +/* sprintf(st,ST_INTRO5, Config::game_version); + new Zone_text(inter, st, 10, 80); + sprintf(st,ST_INTRO12, Config::net_version); + new Zone_text(inter, st, 10, 100);*/ + y=130; + warning=0; + if(!sound) { + (void)new Zone_text(inter, ST_INTRO6, 10, y); + warning++; + y += 20; + } + if(config.warning == 1) { + (void)new Zone_text(inter, ST_INTRO7, 10, y); + y += 20; + } + if(config.warning == 2) { + (void)new Zone_text(inter, ST_INTRO8, 10, y); + warning++; + y += 20; + } + if(config.registered) + (void)new Zone_text(inter, ST_INTRO9, 10, y); + (void)new Zone_text(inter, ST_INTRO10, 10, 430); + (void)new Zone_text(inter, ST_INTRO11, 10, 450); + once = false; +} + +void Menu_intro::init() { + Menu::init(); + if(!config.registered) + call(new Wait_time(500, true)); + call(new Setpalette(pal)); +} + +void Menu_intro::step() { + Menu::step(); + if(!config.registered && !once) { + (void)new Zone_text(inter, ST_INTRO9, 10, 350); + once = true; + } + ret(); + call(new Menu_main()); + call(new Setpalette(noir)); + call(new Wait_time(3000)); +} + +Menu_intro::~Menu_intro() { + delete font2; +} + +Menu_guy::Menu_guy() { + Bitmap *bit; + { + Res_doze res("xlogo.raw"); + Raw pcx(res); + bit = new Bitmap(pcx); + pal.load(pcx); + } + (void)new Zone_bitmap(inter, bit, 0, 0, true); + /*{ + Res_doze res("Raglamp.wav"); + son = new Sample(res, 2); + }*/ +} + +Menu_guy::~Menu_guy() { + //delete son; +} + +void Menu_guy::init() { + Menu::init(); + call(new Fade_to(pal, noir, 64)); +} + +void Menu_guy::step() { + Menu::step(); + exec(new Menu_intro()); + call(new Fade_to(noir, pal, 64)); + call(new Wait_time(300)); + //Sfx stmp(son, 0, 0, 0, 22050); +} + +Menu_ugs::Menu_ugs() { + Bitmap *bit; + { + Res_doze res("Ugs.pcx"); + Pcx pcx(res); + bit = new Bitmap(pcx); + pal.load(pcx); + } + new Zone_bitmap(inter, bit, 0, 0, true); + { + Res_doze res("Flamenco.wav"); + son = new Sample(res, 2); + } +} + +Menu_ugs::~Menu_ugs() { + delete son; +} + +void Menu_ugs::init() { + Menu::init(); + call(new Fade_to(pal, noir, 64)); +} + +void Menu_ugs::step() { + Menu::step(); + exec(new Menu_main()); + call(new Fade_to(noir, pal, 64)); + call(new Wait_time(300)); + Sfx stmp(son, 0, 0, 0, 22050); +} + +void Menu_main_startmusic::init() { + if(config.info.cdmusic == 1) + music->play(1); + if(config.info.cdmusic == 2) + music->play(1, true); + ret(); +} + +Menu_main::Menu_main() { + { + Res_doze res("Debuto.raw"); + Raw raw(res); + pal.load(raw); + } + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + redraw(); +} + +void Menu_main::redraw() { + inter->flush(); + Bitmap *background; + { + Res_doze res("Debuto.raw"); + Raw raw(res); + background = new Bitmap(raw); + } + z_back = new Zone_bitmap(inter, background, 0, 0, true); + + b_help = NULL; + if(config.info.language == 0) { + b_single = new Zone_menu(inter, background, "Debut0.raw", 160, 99); + b_multi = new Zone_menu(inter, background, "Debut1.raw", 166, 139); + b_demo = new Zone_menu(inter, background, "Debut2.raw", 214, 183); + b_tut = new Zone_menu(inter, background, "Debut3.raw", 235, 225); + b_setup = new Zone_menu(inter, background, "Debut4.raw", 221, 267); + b_option = new Zone_menu(inter, background, "DEBUT5.RAW", 264, 310); + if(!Config::xtreme) + b_help = new Zone_menu(inter, background, "DEBUT6.RAW", 261, 351); + b_quit = new Zone_menu(inter, background, "DEBUT7.RAW", 295, 392); + } else { + { + Res_doze res("DEBUTOF.RAW"); + Raw raw(res); + Bitmap bit(raw); + bit.draw(*background, 155, 93); + } + b_single = new Zone_menu(inter, background, "DEBUT0F.RAW", 223, 94); + b_multi = new Zone_menu(inter, background, "DEBUT1F.RAW", 145, 136); + b_demo = new Zone_menu(inter, background, "DEBUT2F.RAW", 174, 175); + b_tut = new Zone_menu(inter, background, "DEBUT3F.RAW", 240, 219); + b_setup = new Zone_menu(inter, background, "DEBUT4F.RAW", 160, 261); + b_option = new Zone_menu(inter, background, "DEBUT5F.RAW", 257, 303); + if(!Config::xtreme) + b_help = new Zone_menu(inter, background, "DEBUT6F.RAW", 223, 344); + b_quit = new Zone_menu(inter, background, "DEBUT7F.RAW", 261, 386); + } + + if(Config::xtreme) { + Res_doze res("DEBUTNR.RAW"); + Raw raw(res); + Bitmap bit(raw); + bit.draw(*background, 227, 345); + } + + sprintf(st, ST_QUADRAVERSION, Config::major, Config::minor, Config::patchlevel); + new Zone_text(inter, st, 460, 430); + if(!config.registered) + new Zone_text(inter, ST_NOTREGISTERED, 460, 454); + old_registered = config.registered; + old_language = config.info.language; + b_logo = new Zone_menu(inter, background, "Debut8.raw", 0, 390); +} + +void Menu_main::init() { + Menu::init(); + call(new Menu_main_startmusic()); + call(new Wait_time(6)); // Pour forcer la palette a se setter AVANT que la musique commence + call(new Setpalette(pal)); + reset_delay(); +} + +void Menu_main::reset_delay() { + delay = 3000; +} + +void Menu_main::step() { + Menu::step(); + delay--; + if(delay < 0) { + call(new Fade_in(pal)); + if(1/*ugs_random.rnd(1) == 0*/) { // always pop a demo (highscore menu sucks) + int i=ugs_random.rnd(3); + char st[20]; + sprintf(st, "demo%02i.rec", i); + call(new Call_setfont(pal, new Demo_multi_player(new Res_compress(st, RES_READ, true), true))); + } + else + call(new Menu_highscore()); + call(new Fade_out(pal)); + reset_delay(); + } + if(result == b_quit || input->quel_key == 1 || quitting) { + input->quel_key = -1; + if(!config.registered) + call(new Menu_quitgame()); + exec(new Fade_out(pal)); + } + if(result == b_tut) { + call(new Fade_in(pal)); + call(new Menu_highscore(-1, NULL, true)); + call(new Fade_out(pal)); + } +#ifdef UGS_DIRECTX + if(result == b_logo) { + call(new Fade_in(pal)); + call(new Menu_internet(ST_HELP20)); // adresse du site web :) + call(new Fade_to(Palette(), pal)); + } +#endif + if(result == b_option) { + call(new Fade_in(pal)); + call(new Menu_option()); + call(new Fade_out(pal)); + } + if(result && result == b_help) { + call(new Fade_in(pal)); + call(new Menu_help()); + call(new Fade_out(pal)); + } + if(result == b_setup) { + call(new Fade_in(pal)); + call(new Menu_setup()); + call(new Fade_out(pal)); + } + if(result == b_multi) { + call(new Fade_in(pal)); + call(new Menu_multi()); + call(new Fade_out(pal)); + } + if(result == b_single) { + call(new Fade_in(pal)); + call(new Single_player(PRESET_SINGLE)); + call(new Fade_out(pal)); + //This sucks, see ya in 1.2.0 (maybe :)) + /*call(new Fade_in(pal)); + call(new Menu_single()); + call(new Fade_out(pal)); + */ + } + if(result == b_demo) { + call(new Fade_in(pal)); + call(new Menu_demo_central()); + call(new Fade_out(pal)); + } +// if(result && result==z_back) { +// Bulle::test(*z_back->actual); +// video->need_paint=2; +// } + if(result) + reset_delay(); + if(old_registered != config.registered || old_language != config.info.language) + redraw(); +} + +Menu_stat::Colonne::Colonne() { + sort_me = false; +} + +void Menu_stat::Colonne::set_titre(const char *s) { + strcpy(titre, s); +} + +Menu_stat::Menu_stat(): +c_start(105), c_gap(4) { + int i; + { + Res_doze res("result.raw"); + Raw raw(res); + bit = new Bitmap(raw); + pal.load(raw); + } + if(config.info.language == 1) { + Res_doze res("RESULTF.RAW"); + Raw raw(res); + Bitmap btemp(raw); + btemp.draw(*bit, 0, 0); + } + pal.set_size(256); + Palette temp; + { + Res_doze res("Fond0.pcx"); + Pcx pcx(res); + temp.load(pcx); + } + for(i=184; i<256; i++) // copie les couleurs des blocs de l'image fond0.pcx + pal.setcolor(i, temp.r(i), temp.g(i), temp.b(i)); + + set_fteam_color(pal); + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + fcourrier[0] = new Font(*fonts.courrier, pal, 255,125,0); + fcourrier[1] = new Font(*fonts.courrier, pal, 0,225,255); + fcourrier[2] = new Font(*fonts.courrier, pal, 255,0,0); + fcourrier[3] = new Font(*fonts.courrier, pal, 255,0,255); + fcourrier[4] = new Font(*fonts.courrier, pal, 255,255,0); + fcourrier[5] = new Font(*fonts.courrier, pal, 0,255,0); + fcourrier[6] = new Font(*fonts.courrier, pal, 0,0,255); + fcourrier[7] = new Font(*fonts.courrier, pal, 170,170,170); + font2 = fteam[4]; + (void)new Zone_bitmap(inter, bit, 0, 0); + b_quit = new Zone_text_button2(inter, bit, font2, ST_QUIT, 340, 455); + Zone_text_button *z; + int x=8, wid=105; + z=new Zone_text_button2(inter, bit, font2, ST_RANK, x, 80, wid-8); + b_page.add(z); + x+=wid; + z=new Zone_text_button2(inter, bit, font2, ST_SPEED, x, 80, wid-8); + b_page.add(z); + x+=wid; + z=new Zone_text_button2(inter, bit, font2, ST_LINES, x, 80, wid-8); + b_page.add(z); + x+=wid; + z=new Zone_text_button2(inter, bit, font2, ST_COMBO, x, 80, wid-8); + b_page.add(z); + x+=wid; + + b_restart = b_stop = NULL; + + init_columns(bit); + + active_sort = -1; + active_page = -1; + change_page(0); + + notify(); + (void)new Chat_interface(inter, pal, bit, 427, 47, 212, 410); + if(game) + game->net_list.add_watch(this); +} + +Menu_stat::~Menu_stat() { + col.deleteall(); + if(game) + game->net_list.remove_watch(this); + for(int i=0; iquel_stat = CS::FRAG; + n->width = 70; + n->set_titre(ST_RESULTFRAG); + col.add(n); + + n=new Colonne(); + n->quel_stat = CS::DEATH; + n->width = 70; + n->set_titre(ST_RESULTDEATH); + col.add(n); + + n=new Colonne(); + n->quel_stat = CS::SCORE; + n->width = 90; + n->set_titre(ST_SHOWSCORE); + col.add(n); + + n=new Colonne(); + n->quel_stat = CS::LINESTOT; + n->width = 90; + n->set_titre(ST_SHOWLINE); + col.add(n); + + int px; + px = c_start; + for(i=0; i<4; i++) { + add_title(*col[i], &px, bit); + col[i]->page = page; + } + + page++; + px = c_start; + n=new Colonne(); + n->quel_stat = CS::PPM; + n->width = 150; + n->set_titre(ST_SHOWPPM); + n->page = page; + add_title(*n, &px, bit); + col.add(n); + + n=new Colonne(); + n->quel_stat = CS::BPM; + n->width = 150; + n->set_titre(ST_SHOWBPM); + n->page = page; + add_title(*n, &px, bit); + col.add(n); + +/* int sta1=0, sta2=0; + { + for(int p=0; pnet_list.get(p); + if(c) { + for(int i=sta1; i<=20; i++) + if(c->stats[CS::clear_trans(i)].get_value()) + sta1=i; + for(int j=sta2; j<=20; j++) + if(c->stats[(CS::Stat_type)(CS::COMBO00+j)].get_value()) + sta2=j; + } + } + sta1 -= 7; + if(sta1<0) + sta1=0; + sta2 -= 7; + if(sta2<0) + sta2=0; + }*/ + page++; + px = c_start; + for(i=0; i<8; i++) { + n=new Colonne(); + n->quel_stat = CS::clear_trans(i); + n->width = 40; + sprintf(st, "%i", i+1); + n->set_titre(st); + n->page = page; + add_title(*n, &px, bit); + col.add(n); + } + + page++; + px = c_start; + for(i=8; i<16; i++) { + n=new Colonne(); + n->quel_stat = CS::clear_trans(i); + n->width = 40; + sprintf(st, "%i", i+1); + n->set_titre(st); + n->page = page; + add_title(*n, &px, bit); + col.add(n); + } + + page++; + px = c_start; + for(i=0; i<8; i++) { + n=new Colonne(); + n->quel_stat = (CS::Stat_type) (CS::COMBO00+i); + n->width = 40; + sprintf(st, "%i", i+1); + n->set_titre(st); + n->page = page; + add_title(*n, &px, bit); + col.add(n); + } + + page++; + px = c_start; + for(i=8; i<16; i++) { + n=new Colonne(); + n->quel_stat = (CS::Stat_type) (CS::COMBO00+i); + n->width = 40; + sprintf(st, "%i", i+1); + n->set_titre(st); + n->page = page; + add_title(*n, &px, bit); + col.add(n); + } +} + +void Menu_stat::add_title(Colonne &col, int *px, Bitmap *bit) { + col.z_titre = new Zone_text_button2(inter, bit, font2, col.titre, *px+3, 120, col.width-6); + col.z_titre->disable(); + *px += col.width; +} + +void Menu_stat::set_sort(int quel) { + if(active_sort == quel) + return; + col[quel]->sort_me = true; + col[quel]->z_titre->set_font(fteam[4]); + + if(active_sort != -1) { + col[active_sort]->sort_me = false; + col[active_sort]->z_titre->set_font(inter->font); + } + active_sort = quel; +} + +void Menu_stat::change_page(int p) { + int i, last_page=0; + for(i=0; ipage == active_page) { + col[i]->z_titre->disable(); + } + if(col[i]->page>last_page) + last_page=col[i]->page; + } + switch(active_page) { + case 0: + case 1: + b_page[active_page]->set_font(inter->font); + break; + case 2: + case 3: + b_page[2]->set_font(inter->font); + break; + case 4: + case 5: + b_page[3]->set_font(inter->font); + break; + } + switch(p) { + case 0: + active_page=0; + b_page[0]->set_font(fteam[4]); + break; + case 1: + active_page=1; + b_page[1]->set_font(fteam[4]); + break; + case 2: + if(active_page==2) + active_page=3; + else + active_page=2; + b_page[2]->set_font(fteam[4]); + break; + case 3: + if(active_page==4) + active_page=5; + else + active_page=4; + b_page[3]->set_font(fteam[4]); + break; + } + if(active_page > last_page) + active_page = 0; + for(i=0; ipage == active_page) { + col[i]->z_titre->enable(); + } + notify(); +} + +void Menu_stat::calculate_total(bool force_blit) { + if(!(overmind.framecount&127) || force_blit) { + bool must_reblit = false; + score.updateFromGame(); + if(score.team_order_changed || score.order_changed) + must_reblit = true; + if(active_sort!=-1) + score.sort(col[active_sort]->quel_stat); + if(score.team_order_changed || score.order_changed) + must_reblit = true; + if(must_reblit || force_blit) + display(); + } +} + +void Menu_stat::display() { + zone.deleteall(); + int y = 145; + for(int loo=0; loonet_list.get(i); + if(c && c->color == team) { + zone.add(new Zone_text(fteam[team], inter, c->long_name(), 2, y)); + + Font *color = fcourrier[team]; + + int px = c_start; + for(int j=0; jpage == active_page) { + zone.add(new Zone_text_numeric(color, inter, score.stats[i].stats[col[j]->quel_stat].get_address(), px, y, col[j]->width-c_gap)); + px += col[j]->width; + } + } + y += 22; + } + } + } + if(score.player_count[team] > 1) { + zone.add(new Zone_text(fteam[team], inter, ST_TOTAL, 15, y)); + int px = c_start; + Font *color = fcourrier[team]; + for(int j=0; jpage == active_page) { + zone.add(new Zone_text_numeric(color, inter, score.team_stats[team].stats[col[j]->quel_stat].get_address(), px, y, col[j]->width-c_gap)); + px += col[j]->width; + } + } + y += 34; + } else if(score.player_count[team] == 1) { + y += 12; // laisse espace entre les joueurs/totaux de chaque team + } + } + video->need_paint = 2; +} + +void Menu_stat::notify() { + calculate_total(true); +} + +void Menu_stat::init() { + set_fteam_color(pal); + set_sort(0); + Menu_standard::init(); +} + +void Menu_stat::step() { + Menu_standard::step(); + bool force_blit=false; + //Add appropriate button + if(game->server && game->network && !game->terminated && !b_stop) + b_stop = new Zone_text_button2(inter, bit, font2, ST_STOPGAME, 8, 455); + //Remove end-of-game button if already terminated + if(b_stop && game->terminated) { + delete b_stop; + b_stop = NULL; + video->need_paint = 2; + } + if(net->active && !game->server && !net->connected()) { + delete b_restart; + b_restart=NULL; + } + if(!playback && !b_restart) + if(game->server && game->terminated) + b_restart = new Zone_text_button2(inter, bit, font2, ST_RESTARTGAME, 8, 455); + else + if(!game->server && net->active && net->connected()) + b_restart = new Zone_text_button2(inter, bit, font2, ST_REJOINGAME, 8, 455); + if(result) { + if(result == b_quit) + quit = true; + if(result == b_stop) { + if(!game->terminated) + game->net_list.send_end_signal(false); + } + if(result == b_restart) { + if(game->server) { + game->stop_stuff(); + game->restart(); + exec(new Call_setfont(pal, new Multi_player_launcher())); + } + else { + //We're not server but we want to restart, drop all players + int i; + for(i=0; inet_list.get(i)) { + Packet_dropplayer p; + p.player=i; + p.reason=DROP_AUTO; + game->net_list.drop_player(&p, false); + } + } + //... and delete the game + delete game; + exec(new Join_game(bit, inter->font, font2, pal, NULL, 0, 0, true)); + } + } + int i; + for(i=0; iz_titre) { + set_sort(i); + force_blit=true; + break; + } + } + for(i=0; iset_font(font, false); + new Zone_bitmap(inter, bit, 0, 0); + new Zone_text(inter, ST_CHECKIPTITLE, 20); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); + new Zone_text(inter, ST_HOSTNAME, 170, 110); + new Zone_text_field(inter, net->host_name, 310, 110, inter->font->width(net->host_name)+8); + new Zone_text(inter, ST_HOSTLIST, 170, 140); + Zone_listbox *list; + list = new Zone_listbox2(inter, bit, font2, NULL, 310, 140, 160, 200); + for(int i=0; ihost_adr.size(); i++) { + Net::stringaddress(st, net->host_adr[i]); + list->add_item(st); + } +} + +void Menu_multi_checkip::step() { + Menu::step(); + if(input->quel_key == 1 || result==cancel) { + input->quel_key = 0; + ret(); + } +} + +Menu_multi_book::Menu_multi_book(Bitmap *bit, Font *font, Font *font2, const Palette& p, const char *adr) { + pal = p; + bit_ = bit; + font2_ = font2; + inter->set_font(font, false); + address = adr; + new Zone_bitmap(inter, bit, 0, 0); + cancel = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 450); + if(address) { // si address de connexion deja fourni + new Zone_text(inter, ST_CONNECT, 20); + } else { + new Zone_text(inter, ST_ADDRESSBOOKTITLE, 20); + for(int i=0; i<10; i++) { + int y = 60 + i*36; + new Zone_text_input(inter, pal, config.info.book[i], 255, 40, y, 460); + b_connect[i] = new Zone_text_button2(inter, bit, font2, ST_CONNECT, 520, y); + } + } + status = new Zone_text_field(inter, "", 6, 450, 540); + looking = false; + connect_failed = false; +} + +void Menu_multi_book::init() { + Menu::init(); + if(address) + call(new Fade_in(pal)); +} + +void Menu_multi_book::step() { + Menu::step(); + if(input->quel_key == KEY_ESCAPE || result==cancel) { + input->quel_key = 0; + if(looking) + net->gethostbyname_cancel(); + config.write(); + ret(); + } + const char *connect_adr=NULL; + + if(address) { + if(!looking && !connect_failed) { + connect_adr = address; + } + } else { + for(int i=0; i<10; i++) { + if(result == b_connect[i] && config.info.book[i][0] != 0) { + connect_adr = config.info.book[i]; + break; + } + } + } + if(connect_adr) { + if(looking) + net->gethostbyname_cancel(); + Dword adr = net->getaddress(connect_adr); + if(adr > 0) { + call(new Join_game(bit_, inter->font, font2_, pal, NULL, adr, net->port_resolve, false)); + if(address) + ret(); + } else { + sprintf(st, ST_LOOKINGFORBOB, connect_adr); + status->set_val(st); + looking = true; + } + } + if(looking) { + if(net->name_resolve != (unsigned int)-1) { + if(net->name_resolve == 0) { + status->set_val(ST_ERRORLOOKING); + looking = false; + connect_failed = true; + } else { + status->set_val(""); + looking = false; + call(new Join_game(bit_, inter->font, font2_, pal, NULL, net->name_resolve, net->port_resolve, false)); + if(address) + ret(); + } + net->name_resolve = (Dword)-1; + } + } +} + +Menu_internet::Menu_internet(const char *c): Menu() { + command = c; + new Zone_clear(inter); +} + +void Menu_internet::init() { + Menu::init(); +} + +void Menu_internet::step() { + Menu::step(); +#ifdef UGS_DIRECTX + ShellExecute(0, "open", command, NULL, NULL, SW_SHOWDEFAULT); +#endif + call(new Wait_time(200)); + ret(); +} + +Menu_startserver::Menu_startserver() { + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + set_fteam_color(pal); + + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + (void)new Zone_clear(inter); +} + +void Menu_startserver::step() { + exec(new Create_game_start(pal, bit, inter->font)); +} + +Menu_startserver::~Menu_startserver() { + delete bit; +} + +Menu_startconnect::Menu_startconnect(const char *adr, bool rejoin) { + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + set_fteam_color(pal); + + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + font2 = new Font(*fonts.normal, pal, 255,255,0); + (void)new Zone_clear(inter); + if(rejoin) + module = new Join_game(bit, inter->font, font2, pal, NULL, 0, 0, true); + else + module = new Menu_multi_book(bit, inter->font, font2, pal, adr); +} + +void Menu_startconnect::step() { + call(new Fade_out(pal)); + exec(module); +} + +Menu_startconnect::~Menu_startconnect() { + if(bit) + delete bit; + delete font2; +} diff --git a/source/menu_base.cpp b/source/menu_base.cpp new file mode 100644 index 0000000..a01620c --- /dev/null +++ b/source/menu_base.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "input.h" +#include "inter.h" +#include "texte.h" +#include "global.h" +#include "zone.h" +#include "quadra.h" +#include "menu_base.h" + +void Menu_quit::step() { + Menu::step(); + if(input->quel_key == 1 || quitting) + quit = true; + if(quit) + exec(new Fade_out(pal)); +} + +Menu_net_problem::Menu_net_problem(const char *s, const char *context, Bitmap *bit, Font *font) { + inter->set_font(font, false); + (void)new Zone_bitmap(inter, bit, 0, 0); + (void)new Zone_text(inter, ST_NETERROR, 120); + if(!s) + s = "Generic network error"; + (void)new Zone_text(inter, s, 160); + (void)new Zone_text(inter, ST_NETERROR2, 200); + if(context) + (void)new Zone_text(inter, context, 240); + cancel = new Zone_text_button2(inter, bit, font, ST_BACK, 560, 450); +} + +void Menu_net_problem::step() { + Menu::step(); + if(input->quel_key == 1 || result==cancel) { + input->quel_key = 0; + ret(); + } +} + +Call_setfont::Call_setfont(const Palette &p, Module *m): pal(p) { + module = m; +} + +void Call_setfont::init() { + call(module); +} + +void Call_setfont::step() { + set_fteam_color(pal); + video->need_paint=2; + ret(); +} diff --git a/source/menu_demo_central.cpp b/source/menu_demo_central.cpp new file mode 100644 index 0000000..7292bcf --- /dev/null +++ b/source/menu_demo_central.cpp @@ -0,0 +1,435 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "inter.h" +#include "bitmap.h" +#include "pcx.h" +#include "dict.h" +#include "clock.h" +#include "res_compress.h" +#include "find_file.h" +#include "fonts.h" +#include "texte.h" +#include "zone.h" +#include "global.h" +#include "quadra.h" +#include "recording.h" +#include "game.h" +#include "multi_player.h" +#include "menu_demo_central.h" + +class Zone_change_dir: public Zone_text_input { + Menu_demo_central *menu_demo_central; +public: + Zone_change_dir(Inter *in, const Palette &p, char *s, int max, int x, int y, int w, Menu_demo_central *m); + virtual void lost_focus(int cancel); +}; + +Zone_change_dir::Zone_change_dir(Inter *in, const Palette &p, char *s, int max, int x, int y, int w, Menu_demo_central *m): + Zone_text_input(in, p, s, max, x, y, w) { + menu_demo_central = m; +} + +void Zone_change_dir::lost_focus(int cancel) { + Zone_text_input::lost_focus(cancel); + if(cancel == 0) + menu_demo_central->reload(); +} + +Menu_demo_central::Listitem::Listitem(const char *n, Font *f): + Listable(n, f) { + file_date[0] = 0; + file_size = 0; +} + +Menu_demo_central::Listitem::~Listitem() { +} + +Menu_demo_central::Player_infos::Player_infos(int pplayer) { + name[0]=0; + player=pplayer; + team=255; +} + +Menu_demo_central::Menu_demo_central() { + { + Res_doze res("Multi.raw"); + Raw img(res); + bit = new Bitmap(img); + pal.load(img); + } + pal.set_size(256); + set_fteam_color(pal); + + fcourrier[0] = new Font(*fonts.courrier, pal, 255,125,0); + fcourrier[1] = new Font(*fonts.courrier, pal, 0,225,255); + fcourrier[2] = new Font(*fonts.courrier, pal, 255,0,0); + fcourrier[3] = new Font(*fonts.courrier, pal, 255,0,255); + fcourrier[4] = new Font(*fonts.courrier, pal, 255,255,0); + fcourrier[5] = new Font(*fonts.courrier, pal, 0,255,0); + fcourrier[6] = new Font(*fonts.courrier, pal, 0,0,255); + fcourrier[7] = new Font(*fonts.courrier, pal, 170,170,170); + + inter->set_font(new Font(*fonts.normal, pal, 255,255,255)); + (void)new Zone_bitmap(inter, bit, 0, 0, true); + (void)new Zone_text(inter, ST_DEMOCENTRAL, 20); + (void)new Zone_text(fteam[7], inter, ST_CURRENTDIRECTORY, 10, 50); + Find_file::get_current_directory(find_directory); + z_dir = new Zone_change_dir(inter, pal, find_directory, 900, 170, 50, 460, this); + z_list = new Zone_listbox2(inter, bit, fteam[4], &quel, 10, 90, 180, 300); + z_status = new Zone_text_field(inter, "", 0, 460, 640); + z_play = new Zone_text_button2(inter, bit, fteam[4], ST_STARTPLAYBACK, 30, 400); + z_delete = new Zone_text_button2(inter, bit, fteam[4], ST_DELETEDEMO, 30, 430); + cancel = new Zone_text_button2(inter, bit, fteam[4], ST_BACK, 560, 430); + int y=90,ys=21; + (void)new Zone_text(fteam[7], inter, ST_DEMONAME, 210, y); + z_name = new Zone_text_field(inter, (char *) NULL, 335, y, 295, fteam[1]); y+=ys; + (void)new Zone_text(fteam[7], inter, ST_DEMODATE, 210, y); + z_date = new Zone_text_field(inter, (char *) NULL, 335, y, 295, fteam[1]); y+=ys; + (void)new Zone_text(fteam[7], inter, ST_DEMOVERSION, 210, y); + z_version = new Zone_text_field(inter, (char *) NULL, 335, y, 70, fteam[1]); y+=ys; + (void)new Zone_text(fteam[7], inter, ST_DEMODURATION, 210, y); + z_duration = new Zone_text_field(inter, (char *) NULL, 335, y, 295, fteam[1]); y+=ys; + (void)new Zone_text(fteam[7], inter, ST_DEMOTYPE, 210, y); + z_type = new Zone_text_field(inter, (char *) NULL, 335, y, 295, fteam[1]); y+=ys; + (void)new Zone_text(fteam[7], inter, ST_DEMOEND, 210, y); + z_end = new Zone_text_field(inter, (char *) NULL, 335, y, 295, fteam[1]); y+=ys; + + play = NULL; + reload(); +} + +Menu_demo_central::~Menu_demo_central() { + for(int i=0; ineed_paint = 2; +} + +void Menu_demo_central::refresh_detail() { + z_date->set_val(s_date); + z_name->set_val(s_name); + z_version->set_val(s_version); + z_duration->set_val(s_duration); + z_type->set_val(s_type); + z_end->set_val(s_end); +} + +void Menu_demo_central::drive_playback(const char *n) { + char temp[1024]; + sprintf(temp, "%s/%s", find_directory, n); + Res_compress *res = new Res_compress(temp, RES_TRY); + if(res->exist) { + play = new Playback(res); + if(play->completed || !play->valid) { + sprintf(st, ST_DEMOBADFILE, temp); + z_status->set_val(st); + delete play; + play = NULL; + } + else { + z_status->set_val(""); + strcpy(s_version, "1.0.1"); + strcpy(s_name, "-"); + strcpy(s_duration, "-"); + strcpy(s_date, "-"); + if(play->packet_gameserver) { + strcpy(s_type, ST_GAMETYPE1); + if(play->packet_gameserver->survivor) + strcpy(s_type, ST_GAMETYPE2); + Attack_type nat=play->packet_gameserver->normal_attack.type; + Attack_type cat=play->packet_gameserver->clean_attack.type; + if(nat==ATTACK_NONE && cat==ATTACK_NONE) + strcpy(s_type, ST_GAMETYPE4); + if(nat==ATTACK_BLIND || nat==ATTACK_FULLBLIND) + strcpy(s_type, ST_GAMETYPE5); + if(play->packet_gameserver->hot_potato) + strcpy(s_type, ST_GAMETYPE3); + switch(play->packet_gameserver->game_end) { + case END_NEVER: + strcpy(s_end, ST_DEMONEVER); + break; + case END_FRAG: + sprintf(s_end, ST_DEMOAFTERFRAG, play->packet_gameserver->game_end_value); + break; + case END_TIME: + sprintf(s_end, ST_DEMOAFTERMINUTE, play->packet_gameserver->game_end_value/6000); + if(play->single()) + strcpy(s_type, ST_GAMETYPE6); + break; + case END_POINTS: + sprintf(s_end, ST_DEMOAFTERSCORE, play->packet_gameserver->game_end_value); + break; + case END_LINES: + sprintf(s_end, ST_DEMOAFTERLINES, play->packet_gameserver->game_end_value); + break; + default: + strcpy(s_end, ST_DEMOUNKNOWN); + break; + } + } + if(play->single()) + strcpy(s_type, ST_DEMOSINGLE); + populate_dict(play->sum); + } + } + else { + sprintf(st, ST_DEMOBADFILE, temp); + z_status->set_val(st); + } + delete res; +} + +void Menu_demo_central::populate_dict(Dict *d) { + if(!d) + return; + const char *temp; + temp = d->find("name"); + if(temp) + strcpy(s_name, temp); + temp = d->find("quadra_version"); + if(temp) + strcpy(s_version, temp); + temp = d->find("duration"); + if(temp) { + int dur = atoi(temp); + int min=dur/6000; + int sec=(dur%6000)/100; + if(min) + sprintf(s_duration, ST_BOBMINUTES, min); + else + s_duration[0]=0; + if(sec) { + char st[100]; + sprintf(st, ST_BOBSECONDS, sec); + if(min) + strcat(s_duration, " "); + strcat(s_duration, st); + } + } + temp = d->find("time"); + if(temp) { + int dur = atoi(temp); + temp = Clock::time2char(dur); + if(temp) + strcpy(s_date, temp); + } + int cx[5], cw[5]; + cx[0] = 210; + cw[0] = 120; + cx[1] = cx[0] + cw[0] + 5; + cw[1] = 60; + cx[2] = cx[1] + cw[1] + 5; + cw[2] = 60; + cx[3] = cx[2] + cw[2] + 5; + cw[3] = 90; + cx[4] = cx[3] + cw[3] + 5; + cw[4] = 80; + int y=220, ys=21, i, j; + zone.add(new Zone_text(fteam[7], inter, ST_PLAYERS, cx[0], y)); + if(!play->single()) { + zone.add(new Zone_text(fteam[7], inter, ST_RESULTFRAG, cx[1], y)); + zone.add(new Zone_text(fteam[7], inter, ST_RESULTDEATH, cx[2], y)); + } + zone.add(new Zone_text(fteam[7], inter, ST_SCORE, cx[3], y)); + zone.add(new Zone_text(fteam[7], inter, ST_LINES, cx[4], y)); y+=ys; + pinfos.deleteall(); + Dict *players = d->find_sub("players"); + if(players && players->size()<=MAXPLAYERS) { + for(i = 0; i < (int)players->size(); i++) { + const Dict *d2 = players->get_sub(i); + Player_infos *pi=new Player_infos(i); + pinfos.add(pi); + const char *name = d2->find("name"); + if(name) + strcpy(pi->name, name); + temp = d2->find("team"); + if(temp) + pi->team = atoi(temp); + } + //Reset score with freshly constructed Score object + Score score2; + score=score2; + score.updateFromDict(players); + Byte team_pos, team; + for(team_pos=0; team_posfont; + if(pi->team<=6) + f=fteam[pi->team]; + zone.add(new Zone_text(f, inter, pi->name, cx[0], y)); + int *statp; + if(!play->single()) { + statp=score.stats[i].stats[CS::FRAG].get_address(); + zone.add(new Zone_text_field(inter, statp, cx[1], y, cw[1], fcourrier[pi->team], false)); + statp=score.stats[i].stats[CS::DEATH].get_address(); + zone.add(new Zone_text_field(inter, statp, cx[2], y, cw[2], fcourrier[pi->team], false)); + } + statp=score.stats[i].stats[CS::SCORE].get_address(); + zone.add(new Zone_text_field(inter, statp, cx[3], y, cw[3], fcourrier[pi->team], false)); + statp=score.stats[i].stats[CS::LINESTOT].get_address(); + zone.add(new Zone_text_field(inter, statp, cx[4], y, cw[4], fcourrier[pi->team], false)); + y+=ys; + } + } + } + } +} + +void Menu_demo_central::step() { + Menu_standard::step(); + if(!result) + return; + if(result == cancel) + quit=true; + if(z_list->in_listbox(result)) { + Listitem *e = (Listitem *) z_list->get_selected(); + if(e && !e->isfolder) { + clear_detail(); + drive_playback(e->list_name); + refresh_detail(); + } else { + clear_detail(); + } + } + Zone *dbl = inter->double_clicked; + if(dbl && z_list->in_listbox(dbl)) { + Listitem *e = (Listitem *) z_list->get_selected(); + if(e && e->isfolder) { + if(!strcmp(e->list_name, "..")) { + // traite le .. differemment + char *t = strrchr(find_directory, '/'); + if(t) + *t = 0; + } + else { + strcat(find_directory, "/"); + strcat(find_directory, e->list_name); + } + z_dir->set_val(find_directory); + reload(); + } + } + if((dbl && z_list->in_listbox(dbl)) || result == z_play) { + Listitem *e = (Listitem *) z_list->get_selected(); + if(e && !e->isfolder) { + if(!play) { + clear_detail(); + drive_playback(e->list_name); + refresh_detail(); + } + if(play) { + play->shit_skipper2000(false); + call(new Fade_in(pal)); + call(new Call_setfont(pal, new Demo_multi_player(play))); + call(new Fade_out(pal)); + // le 'delete play' est fait par ~Demo_multi_player + play = NULL; + } + } + } + if(result == z_delete) { + Listitem *e = (Listitem *) z_list->get_selected(); + if(e && !e->isfolder) { + char temp[1024]; + sprintf(temp, "%s/%s", find_directory, e->list_name); + remove(temp); + z_list->remove_item(e); + } + } +} + +void Menu_demo_central::reload() { + quel = -1; + #ifdef UGS_DIRECTX + do { + char *t = strchr(find_directory, '\\'); + if(t) + *t = '/'; + else + break; + } while(1); + #endif + + z_list->clear(); + clear_detail(); + refresh_detail(); + z_status->set_val(""); + + //Remove slashes from the end + char *temp=find_directory+strlen(find_directory); + while(temp>=find_directory) { + if(*temp == '/') + *temp=0; + else + break; + temp--; + } + z_dir->set_val(find_directory); + char temp_search[1024]; + sprintf(temp_search, "%s/*", find_directory); + + msgbox("Menu_demo_central::find_all: Finding directories in [%s]...\n", temp_search); + { + Find_file *find_file = Find_file::New(temp_search); + z_list->init_sort(); + while(!find_file->eof()) { + Find_file_entry ff = find_file->get_next_entry(); + if(ff.is_folder) { + if(ff.name[0] == '.' && ff.name[1] == 0) // ignore le repertoire "." + continue; + Listitem *e = new Listitem(ff.name, fteam[1]); + e->isfolder = true; + z_list->add_sort(e); + } + } + z_list->end_sort(); + delete find_file; + } + + sprintf(temp_search, "%s/*.rec", find_directory); + + msgbox("Menu_demo_central::find_all: Finding files in [%s]...\n", temp_search); + { + Find_file *find_file = Find_file::New(temp_search); + z_list->init_sort(); + while(!find_file->eof()) { + Find_file_entry ff = find_file->get_next_entry(); + if(!ff.is_folder) { + Listitem *e = new Listitem(ff.name, fteam[5]); + e->file_size = ff.size; + strcpy(e->file_date, ff.date); + e->isfolder = false; + z_list->add_sort(e); + } + } + z_list->end_sort(); + delete find_file; + } +} diff --git a/source/misc.cpp b/source/misc.cpp new file mode 100644 index 0000000..9a53ba0 --- /dev/null +++ b/source/misc.cpp @@ -0,0 +1,72 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "input.h" +#include "random.h" +#include "sons.h" +#include "main.h" +#include "global.h" +#include "misc.h" + +Wait_time::Wait_time(int del, bool force) { + force_wait = force; + delay = del; +} + +void Wait_time::step() { + if(!force_wait) + Wait_event::step(); + delay--; + if(delay < 0 || quitting) + ret(); +} + +void Wait_event::step() { + if(ecran) + ecran->do_frame(); + if(input->quel_key != -1 || (ecran && ecran->clicked)) { + input->quel_key = -1; + ret(); + } +} + +Fade_to::Fade_to(const Palette& dst, const Palette& src, int nframe) { + fad = new Fade(dst, src, nframe); +} + +Fade_to::~Fade_to() { + delete fad; +} + +void Fade_to::step() { + if(quitting) { + ret(); + return; + } + time_control = TIME_FREEZE; // fade toujours sync avec le display + if(fad->step()) + ret(); + fad->set(); +} + +void Fade_in::init() { + Fade_to::init(); + Sfx stmp(sons.fadein, 0, -400, 0, 11000+ugs_random.rnd(511)); +} + +void Fade_out::init() { + Fade_to::init(); + Sfx stmp(sons.fadeout, 0, -400, 0, 22000+ugs_random.rnd(511)); +} + +Setpalette::Setpalette(const Palette& p): pal(p) { +} + +void Setpalette::init() { + Module::init(); + video->setpal(pal); + ret(); +} diff --git a/source/multi_player.cpp b/source/multi_player.cpp new file mode 100644 index 0000000..7c011fe --- /dev/null +++ b/source/multi_player.cpp @@ -0,0 +1,423 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "input.h" +#include "sprite.h" +#include "pcx.h" +#include "zone.h" +#include "config.h" +#include "quadra.h" +#include "pane.h" +#include "chat_text.h" +#include "music.h" +#include "game.h" +#include "global.h" +#include "texte.h" +#include "net_stuff.h" +#include "recording.h" +#include "sons.h" +#include "main.h" +#include "menu.h" +#include "fonts.h" +#include "highscores.h" +#include "canvas.h" +#include "net_server.h" +#include "nglog.h" +#include "clock.h" +#include "multi_player.h" + +Multi_player::Multi_player(int *got_high) { + stop=false; //set to true to quit game + got_highscore = got_high; + last_countdown = -1; + menu_stat = NULL; + int i; + { + Res_doze res("Fond0.pcx"); + Pcx img(res); + bit = new Bitmap(img); + } + pal.set_size(256); + for(i=0; i<9; i++) + color[i] = new Color(i, pal); + font2 = fteam[4]; + if(game->single) { + if(config.info.cdmusic == 1) + music->play(2); + i = 1; + } else { + i = game->get_multi_level(); + if(config.info.cdmusic == 1) + music->play(i+1, true); + } + Canvas::change_level(i, &pal, bit); + set_fteam_color(pal); + inter->set_font(new Font(*fonts.normal, pal, 255, 255, 255)); + courrier = new Font(*fonts.courrier, pal, 255, 255, 255); + (void)new Zone_bitmap(inter, bit, 0, 0); + input->pause = pause = false; + zone_pause = NULL; + + for(i=0; i<3; i++) { + pane_exec[i] = new Executor; + pane_info[i] = new Pane_info(bit, font2, inter, i, this); + if(game->single) { + switch(i) { + case 0: pane[i] = new Pane_blockinfo(*pane_info[i]); break; + case 1: + if(playback && playback->old_mode) + pane[i] = new Pane_startgame(*pane_info[i], playback->single_player); + else + pane[i] = new Pane_singleplayer(*pane_info[i]); + break; + case 2: pane[i] = new Pane_comboinfo(*pane_info[i]); break; + } + } else { // sinon mode multi_player + pane[i] = new Pane_option(*pane_info[i]); + } + pane_exec[i]->add(pane[i]); + overmind.start(pane_exec[i]); + } + if(playback && playback->single() && !playback->auto_demo) { + (void)new Zone_slow_play(inter, bit, font2, ST_SLOWPLAY, 430, 420); + (void)new Zone_fast_play(inter, bit, font2, ST_FASTPLAY, 430, 450); + b_quit = new Zone_text_button2(inter, bit, font2, ST_BACK, 560, 455); + } + else { + b_quit=NULL; + } +} + +void Multi_player::step() { + Menu_fadein::step(); + +/* Generate small pics for ngStatsQuadra + static Dword wait_timer=0; + wait_timer++; + if(wait_timer==500) { + int col; + Byte side; + for(col=-1; col<9; col++) { + for(side=0; side<16; side++) { + char st[32]; + Bitmap the_bit(18, 18, 18); + if(col!=-1) { + raw_draw_bloc(video->vb, 0, 0, side, color[col]); + video->vb->get_bitmap(&the_bit, 0, 0, 18, 18); + sprintf(st, "%c%c.raw", '0'+col, 'a'+side); + } + else + strcpy(st, "e0.raw"); + Raw raw(18, 18, col!=-1? 8:2); + Res_dos res(st, RES_CREATE); + if(!res.exist) { + skelton_msgbox("Can't create file!\n"); + return; + } + raw.write(res); + int i; + Byte pa[3]; + if(col!=-1) + for(i=color[col]->shade(0); ishade(0)+8; i++) { + pa[0] = pal.r(i); + pa[1] = pal.g(i); + pa[2] = pal.b(i); + res.write(pa, 3); + } + else { + pa[0]=0; + pa[1]=0; + pa[2]=0; + res.write(pa, 3); + pa[0]=255; + pa[1]=255; + pa[2]=255; + res.write(pa, 3); + } + for(i=0; i<18; i++) { + Byte pel[18]; + memcpy(pel, the_bit[i], 18); + for(int j=0; j<18; j++) + pel[j] = pel[j] & 7; + res.write(pel, 18); + } + } + } + }*/ + + if(playback && playback->auto_demo) + if(result || input->quel_key != -1) { + input->quel_key = -1; + stop = true; + } + + check_pause(); + + if(playback && playback->old_mode) { + if(playback->completed) { + game->endgame(); + } + } + if(game->single && game->terminated && game->net_list.all_gone()) { + Canvas *c=game->net_list.get(0); + if(recording && c) { + recording->end_multi(); + game->net_server->stop_multi_recording(); + *got_highscore = Highscores::update(c); + } + stop = true; + } + if(input->quel_key == KEY_ESCAPE) + game->abort = true; + if(game->abort) + stop = true; + if(game->terminated && game->net_list.all_gone()) + stop = true; + if(playback && result && result == b_quit) + stop = true; + if(stop) { + if(game->server && !game->network) { + game->net_list.send_end_signal(!game->abort); + } + if(!game->single && menu_stat) + *menu_stat = new Menu_stat(); + exec(new Fade_out(pal)); + return; + } + + if(_debug) { + int snap_can=-1; + if(input->keys[KEY_F2] & PRESSED) { + input->keys[KEY_F2] = 0; + snap_can = 0; + } + if(input->keys[KEY_F3] & PRESSED) { + input->keys[KEY_F3] = 0; + snap_can = 1; + } + if(input->keys[KEY_F4] & PRESSED) { + input->keys[KEY_F4] = 0; + snap_can = 2; + } + if(snap_can != -1) { + video->vb->rect(snap_can * 214+142, 0, 60, 30, 0); + video->snap_shot(snap_can * 214+6, 0, 203, 401); + } + } +} + +Multi_player::~Multi_player() { + int i; + msgbox("Multi_player::~Multi_player\n"); + if(zone_pause) + delete zone_pause; + for(i=0; i<3; i++) { + pane[i] = NULL; // Tres important car les delete pane_exec[i] detruisent des Pane + // qui accedent a pane[] + overmind.stop(pane_exec[i]); + delete pane_exec[i]; + delete pane_info[i]; + } + for(i=0; i<9; i++) + delete color[i]; + delete bit; + delete courrier; + if(!game->single && !playback) + config.write(); // save la config seulement en multi-player ET pas dans une demo +} + +void Multi_player::check_pause() { + if(input->pause) { + bool ignore = false; + if(game->delay_start != 0 && game->delay_start != 500) + ignore = true; + if(!ignore) + game->clientpause(); + input->pause=false; + } + if(game->paused != pause) { + pause = game->paused; + if(pause) { + if(game->delay_start == 0) // empeche de faire le son au debut + Sfx stmp(sons.pause, 0, -300, 0, 11025); + zone_pause = new Zone_sprite(inter, "GAMEPAUS.RAW"); + } else { + if(zone_pause) + delete zone_pause; + zone_pause = NULL; + video->need_paint = 2; + } + } else { + if(game->delay_start && game->delay_start != 500) { + if(game->delay_start == 1) { + Sfx stmp(sons.start, 0, -300, 0, 11025); + return; + } + int chiffre = game->delay_start / 100; + if(chiffre != last_countdown) { + if(zone_pause) + delete zone_pause; + video->need_paint = 2; + sprintf(st, "GAME_%i.RAW", chiffre+1); + zone_pause = new Zone_sprite(inter, st); + last_countdown = chiffre; + Sfx stmp(sons.pause, 0, -300, 0, 20025); + } + } + } +} + +void Multi_player::set_menu_stat(Module **module) { + menu_stat = module; +} + +Demo_multi_player::Demo_multi_player(Res *r, bool auto_demo) { + playback = new Playback(r); + delete r; + playback->auto_demo = auto_demo; +// if(playback->auto_demo) + playback->shit_skipper2000(true); + init_playback(); +} + +Demo_multi_player::Demo_multi_player(Playback *p) { + playback = p; + init_playback(); +} + +void Demo_multi_player::init_playback() { + if(!playback->valid) + return; + playback->create_game(); + playback->multi_level = config.info.multi_level; + if(playback && playback->valid) { + if(game) + game->seed = playback->seed; + } + if(game) + game->frame_start = overmind.framecount; // re-ajuste le frame_start avant de commencer le playback +} + +Demo_multi_player::~Demo_multi_player() { + delete playback; + playback=NULL; + if(game) { + delete game; + if(chat_text) + chat_text->clear(); + } +} + +void Demo_multi_player::init() { + Module::init(); + if(playback && playback->valid && !playback->completed) { + if(playback->single() || playback->auto_demo) + exec(new Multi_player(NULL)); + else + exec(new Multi_player_launcher()); + } + else + ret(); +} + +Single_player::Single_player(Game_preset pgp): gp(pgp) { + play_again = 1; +} + +void Single_player::step() { + if(play_again) { + play_again = 0; + call(new Single_player_iterate(&play_again, gp)); + } + else { + ret(); + } +} + +Single_player_iterate::Single_player_iterate(int *play, Game_preset pgp): gp(pgp) { + hscore = -1; + play_again = play; +} + +void Single_player_iterate::init() { + Game_params p; + p.name=""; + p.set_preset(gp); + (void)new Game(&p); + if(!playback) { + game->loopback_connection=net->start_loopback_client(); + game->loopback_connection->joined=true; + game->loopback_connection->trusted=true; + char fn[1024]; + sprintf(fn, "%s/last.rec", quadradir); + game->prepare_recording(fn); + game->prepare_logging(Clock::absolute_time()); + } + call(new Multi_player(&hscore)); +} + +void Single_player_iterate::step() { + if(game) { + delete game; + if(chat_text) + chat_text->clear(); + } + exec(new Menu_highscore(hscore, play_again, true)); +} + +Zone_slow_play::Zone_slow_play(Inter *in, Bitmap *bit, Font *f, const char *t, int px, int py): + Zone_text_button2(in, bit, f, t, px, py) { +} + +void Zone_slow_play::waiting() { + Zone_text_button2::waiting(); + if(input->mouse.button[0] & PRESSED) { + time_control = TIME_SLOW; + } +} + +void Zone_slow_play::process() { + Zone_text_button2::process(); + if(high && (input->keys[KEY_ENTER] & PRESSED || input->keys[KEY_SPACE] & PRESSED)) { + time_control = TIME_SLOW; + } +} + +Zone_fast_play::Zone_fast_play(Inter *in, Bitmap *bit, Font *f, const char *t, int px, int py): + Zone_text_button2(in, bit, f, t, px, py) { +} + +void Zone_fast_play::waiting() { + Zone_text_button2::waiting(); + if(input->mouse.button[0] & PRESSED) { + time_control = TIME_FAST; + } +} + +void Zone_fast_play::process() { + Zone_text_button2::process(); + if(high && (input->keys[KEY_ENTER] & PRESSED || input->keys[KEY_SPACE] & PRESSED)) { + time_control = TIME_FAST; + } +} + +void Multi_player_launcher::init() { + menu = NULL; + if(!game || !game->terminated) { + Multi_player *multi = new Multi_player(NULL); + multi->set_menu_stat(&menu); + call(multi); + } + else + call(new Menu_stat()); +} + +void Multi_player_launcher::step() { + if(menu) + call(menu); + ret(); +} diff --git a/source/net_list.cpp b/source/net_list.cpp new file mode 100644 index 0000000..e672249 --- /dev/null +++ b/source/net_list.cpp @@ -0,0 +1,1809 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "types.h" +#include "error.h" +#include "canvas.h" +#include "net.h" +#include "chat_text.h" +#include "game.h" +#include "net_stuff.h" +#include "texte.h" +#include "global.h" +#include "sons.h" +#include "main.h" //For alt_tab +#include "recording.h" +#include "net_server.h" +#include "quadra.h" +#include "nglog.h" +#include "net_list.h" + +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif + +//Objectives are number of remaining goals to reach before it is +// announced. Must end with 0. +static int frag_objectives[] = { + 20, 10, 5, 4, 3, 2, 1, 0 +}; + +static int score_objectives[] = { + 200000, 100000, 75000, 50000, 25000, 10000, 5000, 0 +}; + +static int line_objectives[] = { + 200, 100, 75, 50, 25, 10, 5, 0 +}; + +int Net_list::check_goals(Byte team, int remain) { + if(!objectives) //No objectives, no goal is achievable + return 0; + int ret=0; + int *i=objectives; + while(*i && remain<=*i) { + if(!reached[i-objectives][team]) { + reached[i-objectives][team]=true; + ret=*i; + } + i++; + } + //Mark as unattained objectives beyond current remain + // (in case a player is dropped, causing a team's + // goal count to drop suddenly). + while(*i) { + reached[i-objectives][team]=false; + i++; + } + return ret; +} + +void Net_list::reset_objectives() { + Byte obj, team; + for(obj=0; obj<10; obj++) + for(team=0; teamgame_end) { + case END_NEVER: goal_stat=CS::FRAG; objectives=NULL; break; + case END_FRAG: goal_stat=CS::FRAG; objectives=frag_objectives; break; + case END_TIME: goal_stat=CS::FRAG; objectives=NULL; break; + case END_POINTS: goal_stat=CS::SCORE; objectives=score_objectives; break; + case END_LINES: goal_stat=CS::LINESTOT; objectives=line_objectives; break; + default: goal_stat=CS::FRAG; objectives=NULL; break; + } + if(!game->any_attack() && goal_stat==CS::FRAG) { + msgbox("Net_list::reset_objectives: goal_stat==FRAG inconsistent with peace game type, forcing SCORE\n"); + goal_stat=CS::SCORE; + objectives=NULL; + } + //Makes goal_stat the first sort criteria, so that the team in + // score.team_order[0] will always be the closest to victory. + score.sort(goal_stat); +} + +void Net_list::restart() { + end_signaled = false; + reset_objectives(); + syncpoint = Canvas::LAST; + winner_signaled = false; +} + +void Net_list::sendlines(Packet_lines *p) { + Canvas *c=get(p->player); + Canvas *sender=NULL; + if(p->sender!=255) + sender=get(p->sender); + if(c) { + c->add_packet(sender, p->nb, p->nc, p->lx, p->attack, p->hole_pos); + } +} + +void Net_list::send(Canvas *c, Byte nb, Byte nc, Byte lx, Attack attack, bool clean) { + msgbox("Net_list::send\n"); + Packet_clientlines p; + p.sender=c->num_player; + if(game->net_version()>=23) + p.lx=127; //Magic code that says new complexity info is present + else + p.lx=lx; + //Fill in new info anyway + int i, j; + for(j=0; jmoved[j][i]) + p.hole_pos[j] |= 1; + } + } + for(i=0; iislocal()) + continue; + //Sender and receiver are on the same team, don't send + if(receiver->color == c->color) + continue; + p.player=i; + p.attack = attack; + //Potato special rules + if(game->hot_potato) { + Byte potato_team=c->potato_team_on_last_stamp; + //There wasn't a potato team at time of last stamp, don't + // send any lines + if(potato_team==255) + continue; + //If neither receiver or sender are on the potato team, + // don't send the lines + if(receiver->color!=potato_team && c->color!=potato_team) + continue; + } + //Consider handicap difference between players + if(game->net_version()<23) { + // Note that if the sender has a higher handicap, the + // multiplier is always 1. When the receiver has the higher + // handicap, the multiplier is (handicap difference)/2+1. + // Complexity is also multiplied to prevent an handicapped + // receiver from counter-attacking too easily. + int multiplier=0; + if(receiver->handicap>c->handicap) + multiplier=receiver->handicap-c->handicap; + p.nb=nb+(nb*multiplier+1)/2; //round up + p.nc=nc+(nc*multiplier+1)/2; + if(multiplier && p.nc>8) //Avoid "bad" complexity counts + p.nc=8; + } + if(game->net_version()>=23) { + //New handicap code: handicapees send less lines instead of + // receiving more. + int diff=0; + if(c->handicap>receiver->handicap && !clean) + diff=c->handicap-receiver->handicap; + if(nb>diff) + p.nb=nb-diff; + else + p.nb=0; + p.nc=nc; + } + if(p.nb) { + sendlines(&p); + net->sendtcp(&p); + } + } +} + +Net_list::Net_list() { + ppm_limit=0; + lag_limit=3000; + gone_time_limit=18000; + admin_password[0]=0; + motd[0]=0; + int i; + for(i=0; iret(); //overmind will delete it correcly +} + +void Net_list::notify_all() { + for(int i=0; iidle; + else + idle_on_last_notify[i]=0; + Observable::notify_all(); + if(lastgameinfo>1000) + lastgameinfo=500; + msgbox("Net_list::notify_all: done\n"); +} + +int Net_list::add_player(Canvas *c) { + msgbox("Net_list::add_player\n"); + Byte smallest=0xFF; + if(!game->single) { + Dword smallest_frame=0xFFFFFFFF; + for(int i=0; iid(); + delete list[pos]; + msg=false; + } + if(msg) { + char name[256]; + if(c->handicap!=2) { + const char *h; + switch(c->handicap) { + case 0: h=ST_BEGINNER; break; + case 1: h=ST_APPRENTICE; break; + case 2: h=ST_INTERMEDIATE; break; + case 3: h=ST_MASTER; break; + default: + case 4: h=ST_GRANDMASTER; break; + } + sprintf(name, "%s (%s)", c->name, h); + } + else + strcpy(name, c->name); + sprintf(st, ST_BOBJOIN, name, team_name[c->color]); + message(-1, st); + } + //If a canvas was replaced, keep it's id for the new canvas + if(id!=0) + c->set_id(id); + list[pos]=c; + last_use[pos]=overmind.framecount; + c->num_player = pos; +// if(game->paused) +// c->over->pause(); + notify_all(); +} + +void Net_list::remove_player(Canvas *c) { + msgbox("Net_list::remove_player(%08X, player=%i)\n", c, c->num_player); + for(int i=0; istep(); + + { + Packet_serverrandom *p=(Packet_serverrandom *) game->peekpacket(P_SERVERRANDOM); + if(p) { + game->set_seed(p); + game->removepacket(); + } + } + //This sucks, see ya in 1.2.0 (maybe :)) + /*{ + Packet_servernameteam *p=(Packet_servernameteam *) game->peekpacket(P_SERVERNAMETEAM); + if(p) { + set_team_name(p->team, p->name); + game->removepacket(); + } + }*/ + + static Byte syncframes=0; + //Look for a new syncpoint + if(syncframes>=5) { + Packet_serverstate *pst=(Packet_serverstate *) game->peekpacket(P_SERVERSTATE); + if(pst) { + //Found a syncpoint + syncpoint=pst->state; + game->removepacket(); + syncframes=0; + } + } + else + syncframes++; + + //Flush packets from dropped players that may be on the stack + bool got_one; + do { + got_one=false; + Packet *p2=game->peekpacket(255); + Packet_playerbase *ppb=NULL; + if(p2) { + switch(p2->packet_id) { + case P_STAT: + case P_DROPPLAYER: + case P_STAMPBLOCK: + case P_DEAD: + case P_RESPAWN: + case P_STARTWATCH: + case P_DOWNLOAD: + case P_LINES: + case P_FIRST_FRAG: + case P_GONE: + case P_REJOIN: + case P_MOVES: + case P_STATE: + case P_REMOVEBONUS: + ppb=(Packet_playerbase *) p2; + } + } + if(ppb && !get(ppb->player)) { + msgbox("Net_list::step_all: got a Packet_playerbase but player has been dropped, ignoring.\n"); + game->removepacket(); + got_one=true; + } + } while(got_one); + + Packet_lines *p; + while((p = (Packet_lines *)game->peekpacket(P_LINES))) { + msgbox(" sendlines, player=%i, nb=%i, nc=%i, lx=%i\n", p->player, p->nb, p->nc, p->lx); + sendlines(p); + game->removepacket(); + } + if(game->network || !alt_tab) { + game->count_playing_time(); + for(i=0; iover->step(); + } + } + } + score.updateFromGame(); + if(playback && !playback->old_mode) { + Demo_packet dp=playback->next_packet(); + if(dp.p) { + if(dp.frame <= overmind.framecount-game->frame_start || dp.p->packet_id==P_MOVES) { + Net_callable *nc=game->net_client; + if(dp.p->packet_id==P_CHAT) + nc=chat_text; + if(nc) + nc->net_call(dp.p); + playback->remove_packet(); + } + } + else { + if(!demo_completed) { + message(-1, ST_DEMOCOMPLETED); + demo_completed=true; + } + } + } + + check_admin(); + //check_first_frag tells us whether it's a good time to end the + // game, which will be done by check_end_game if all other + // end-game conditions are met + bool end_it=check_first_frag(); + check_end_game(end_it); + check_pause(); + check_drop(); + check_potato(); + check_gone(); + check_player(); + check_stat(); + + //Send stat refreshes every lag_limit/2 or 1500 frames + static Dword stat_timer=0; + if(game->server) { + bool reset_timer=false; + stat_timer++; + for(i=0; iconnections.size(); i++) { + Net_connection *nc=net->connections[i]; + if(stat_timer>=(lag_limit? lag_limit/2 : 1500)) { + if(nc!=game->loopback_connection && nc->packet_based && nc->joined) { + Packet_gamestat p; + p.add_stat(GS::PLAYING_TIME, game->stats[GS::PLAYING_TIME].get_value()); + p.add_stat(GS::ROUND_NUMBER, game->stats[GS::ROUND_NUMBER].get_value()); //why not? + net->sendtcp(nc, &p); + int j; + for(j=0; jstats[CS::PLAYING_TIME].get_value()); + net->sendtcp(nc, &p); + } + } + } + reset_timer=true; + } + if(lag_limit && nc->incoming_inactive>lag_limit/2 && nc->packet_based && nc->joined) { + //Keep clients talking + Packet_servertestping p; + nc->sendtcp(&p); + } + } + if(reset_timer) + stat_timer=0; + } + //Drop laggy connections + if(game->server && lag_limit) { + for(i=0; iconnections.size(); i++) { + Net_connection *nc=net->connections[i]; + if(nc->incoming_inactive>lag_limit && nc!=game->loopback_connection && nc->packet_based && nc->joined && !nc->trusted) { + send_msg(nc, "Your connection has been dropped for exceeding the lag limit on this server."); + send_msg(nc, "You can try to join again to resume playing or find a closer server."); + nc->disconnect(); + } + } + } + //Drop players who exceed the ppm_limit after 4 minutes of + // gameplay + if(game->server && ppm_limit) { + for(i=0; itrying_to_drop && c->stats[CS::PLAYING_TIME].get_value()>=24000 && (Dword)(c->stats[CS::SCORE].get_value())>4*ppm_limit) { + if(!c->remote_adr || c->remote_adr->trusted) + continue; + server_drop_player(i, DROP_AUTO); + send_msg(c->remote_adr, "%s: you have been dropped for exceeding the PPM (points per minute) limit on this server.", c->name); + send_msg(c->remote_adr, "Please join an expert server."); + } + } + } + //Drop players that have been gone for a while without + // maintaining a connection (do that every 2.55 seconds) + if(game->server && gone_time_limit && !game->terminated && !(overmind.framecount&255)) { + for(i=0; iidle==3) { + if(c->gone_time>gone_time_limit) { + if(!c->remote_adr) { + server_drop_player(i, DROP_AUTO); + } + } + } + } + } + //Re-enable acceptconnects if dedicated and no connections + // video_is_dumb==true is assumed to mean dedicated + if(video_is_dumb && net->connections.size()<=1) + game->server_accept_connection = 0; + + //notify_all if anybody has gone + static Dword gone_notify_timer=0; + for(i=0; iidle==3 && idle_on_last_notify[i]<3) + break; + } + if(i=1000) + notify_all(); + } + else + gone_notify_timer=0; + + //Manage gameinfo + if(!lastgameinfo) { + game->sendgameinfo(false); + if(size()) + lastgameinfo=3000; + else + lastgameinfo=15000; + } + else + lastgameinfo--; + game->stepgameinfo(); +} + +void Net_list::check_end_game(bool end_it) { + if(game->server && game->terminated && all_gone() && !game->single) { + //Wait 15 seconds for famous last words + static int restart_timer=1500; + if(restart_timer) { + restart_timer--; + } + else { + //Stop recording and logging + game->stop_stuff(); + if(game->auto_restart) { + game->restart(); + //Return if restart was successful + if(!game->terminated) { + restart_timer=1500; //Reset timer for next time + return; + } + } + else { + //Quit when -dedicated -once and all connections are gone + if(net->connections.size()==1 && video_is_dumb) + quit_fast(); + } + } + } + bool should_end=false; + //Assume there's at least some time left if paused + int time_left=1; + if(!game->terminated && !end_signaled && !game->paused) { + if(game->game_end == END_TIME) { + time_left = game->game_end_value - gettimer(); + switch(time_left) { + case 6000: + case 3000: + case 2000: + case 1000: + case 500: + case 400: + case 300: + case 200: + case 100: + sprintf(st, ST_SECONDSREMAINING, time_left/100); + message(-1, st); + { Sfx stmp(sons.depose4, 0, -300, -1, 22500 - time_left); } + break; + } + } + } + Byte leading_team=score.team_order[0]; + int leading_total=score.team_stats[leading_team].stats[goal_stat].get_value(); + bool draw=false; + bool something_changed=false; + //Check whether second team has same total: draw + Byte second_team=score.team_order[1]; + if(score.player_count[leading_team] && score.player_count[second_team]) + if(score.team_stats[second_team].stats[goal_stat].get_value() == leading_total) + draw=true; + Byte team; + if(game->game_end == END_FRAG || game->game_end == END_POINTS || game->game_end == END_LINES) { + for(team=0; teamgame_end_value-team_goals; + int goal_attained=check_goals(team, remaining); + if(goal_attained && team_goals && remaining>0) { + team2name(team, st); + char unit[64]; + int freq_change; + switch(game->game_end) { + case END_FRAG: strcpy(unit, ST_FRAG); freq_change = 100 * remaining; break; + case END_POINTS: strcpy(unit, ST_POINT); freq_change = (int)(0.01 * remaining); break; + case END_LINES: strcpy(unit, ST_LINE); freq_change = 10 * remaining; break; + default: strcpy(unit, "frog"); freq_change = 0; break; + } + if(remaining!=1) + strcat(unit, "s"); + char st2[256]; + sprintf(st2, ST_BOBBOBSREMAINING, remaining, unit); + strcat(st, st2); + message(-1, st); + { Sfx stmp(sons.depose4, 0, -300, -1, 22500 - freq_change); } + } + } + } + + bool could_end=false; + if(game->game_end == END_TIME && time_left<=0) { + //Timed game with time exhausted, could end + could_end=true; + } + if(leading_total >= game->game_end_value && (game->game_end == END_FRAG || game->game_end == END_POINTS || game->game_end == END_LINES)) { + //Counted game with goal reached, could end + could_end=true; + } + if(game->single) { + //Single-player is dead and done dying, could end + Canvas *c=get(0); + if(c && c->idle==2 && !c->dying) + could_end=true; + } + + if(could_end) + if(draw) { + if(something_changed || (game->game_end==END_TIME && time_left==0)) { + //Drawn at a total higher or equal to end_value: suspense! + // or maybe timer just elapsed and the game is drawn + // (still suspense! :)) + sprintf(st, ST_GAMETIED); + message(-1, st); + { Sfx stmp(sons.levelup, 0, 0, -1, 18050); } + { Sfx stmp(sons.levelup, 0, 0, -1, 18100); } + { Sfx stmp(sons.levelup, 0, 0, -1, 18150); } + } + } + else + should_end=true; + + if(!end_it) { + //Don't signal the end of the game if it's not a good time + // (i.e. we're in the middle of a survivor round) + should_end=false; + } + if(!winner_signaled && game->terminated && all_gone()) { + //Winner found for the first time, declare winner + winner_signaled=true; + char *team="none"; + if(score.team_stats[leading_team].stats[CS::SCORE].get_value()) { + char st[256]; + team2name(leading_team, st); + strcat(st, ST_WINSGAME); + message(-1, st); + team=log_team(leading_team); + } + log_step("playing_end\t%s", team); +/* for(int i=0; istats[CS::LINESTOT].get_value(); + int total_sent=0; + int s; + for(s=CS::CLEAR01; sstats[s].get_value(); + for(s=CS::CLEAR14; sstats[s].get_value(); + char st[256]; + sprintf(st, "%s: %.2f", c->name, total_sent*100.0/total_cleared); + message(c->color, st); + } + }*/ + } + if(game->server) + if(should_end) + send_end_signal(true); + Packet_endgame *p=(Packet_endgame *) game->peekpacket(P_ENDGAME); + if(p) { + char *reason; + if(p->auto_end) + reason="auto"; + else + reason="manual"; + game->removepacket(); + game->endgame(); + message(-1, ST_GAMEEND); + Sfx stmp(sons.start, 0, -300, 0, 11025); + log_step("playing_end_signal\t%s", reason); + } +} + +void Net_list::send_end_signal(bool auto_end) { + if(!game->server) + return; + if(end_signaled) + return; + msgbox("Net_list::send_end_signal: end signaled.\n"); + end_signaled=true; + Packet_endgame p; + p.auto_end=auto_end; + net->dispatch(&p, P_ENDGAME); + if(game->net_server) + game->net_server->record_packet(&p); +} + +void Net_list::check_potato() { + game->check_potato(); + int i; + for(i=0; icolor==game->potato_team) { + c->team_potato_lines=0; + int j; + for(j=0; jcolor==c->color) + c->team_potato_lines+=c2->potato_lines; + } + c->team_potato_linestot=game->potato_lines[game->potato_team]; + } + else { + c->team_potato_lines=0; + c->team_potato_linestot=0; + } + } + } +} + +bool Net_list::check_first_frag() { + if(!game->survivor) + return true; + if(!game->server) + return true; + if(game->delay_start) //Not started yet, do nothing + return true; + bool ret=false; + if(game->game_end == END_TIME) { + //Timed games could always end + ret=true; + } + int i; + bool allwaiting=true; + bool onewaiting=false; + for(i=0; iidle<3) + if(c->state == Canvas::WAITFORWINNER) + onewaiting=true; + else + allwaiting=false; + } + if(allwaiting && onewaiting) { + syncto(Canvas::WAITFORRESTART); + ret=true; //We just finished a round, maybe we should end the game? + } + allwaiting=true; + onewaiting=false; + for(i=0; iidle<3) + if(c->state==Canvas::WAITFORRESTART) + onewaiting=true; + else + allwaiting=false; + } + if(allwaiting && onewaiting) { + if(syncpoint==Canvas::WAITFORRESTART) { + Packet_serverrandom *p=new Packet_serverrandom(); + Random rand; + p->seed=rand.get_seed(); + net->dispatch(p, P_SERVERRANDOM, game->loopback_connection); + if(game->net_server) + game->net_server->record_packet(p); + game->set_seed(p); + delete p; + } + syncto(Canvas::PLAYING); + game->reset_potato(); + } + allwaiting=true; + onewaiting=false; + bool all_gone=true; + for(i=0; iidle!=3) + all_gone=false; + if(c && c->idle<3) + if(c->state==Canvas::PLAYING) + onewaiting=true; + else + allwaiting=false; + } + if(allwaiting && onewaiting || all_gone) + syncto(Canvas::LAST); + int alive_team=-1; + for(i=0; iidle<2) { + alive_team=c->color; + break; + } + } + char st1[32], st[256]; + if(alive_team!=-1) + team2name(alive_team, st1); + else + strcpy(st1, ST_NOBODY); + sprintf(st, ST_BOBWINSROUND, st1); + if(syncpoint==Canvas::LAST) { + if(competitive()) { + if(!game->valid_frag) { + game->valid_frag=true; + game->stats[GS::ROUND_NUMBER].add(1); + log_step("round_start"); + } + } + else { + bool round_end; + if(game->net_version()>=23) + round_end=would_be_competitive(); + else + round_end=game->valid_frag; + if(round_end) { + message(alive_team, st); + //Not competitive but some people are ready to jump in: + // tell winner(s) to first frag. + syncto(Canvas::WAITFORWINNER); + if(game->valid_frag) + log_step("round_end\t%s", log_team(alive_team)); + game->valid_frag=false; + } + } + } + return ret; +} + +void Net_list::team2name(Byte team, char *st) { + if(team>=MAXTEAMS) { + st[0]=0; + return; + } + int count=0; + Canvas *c2=NULL; + int i; + for(i=0; icolor==team) { + c2=c; + if(count++) + break; + } + } + if(count==1) + strcpy(st, c2->name); + else + sprintf(st, ST_BOBTEAM, team_name[team]); +} + +void Net_list::update_team_names() { + //This sucks, see ya in 1.2.0 (maybe :)) + return; + if(!game->server) + return; + if(game->net_version()<23) + return; + bool changed=false; + int team, i; + for(team=0; teamcolor==team) { + found_one=true; + if(c->team_name[0]) { + if(found_no_name) { + found_name=false; + break; + } + if(found_name) { + if(strcmp(name, c->team_name) || memcmp(hash, c->team_hash, sizeof(hash))) { + //Already had a name but this one is different + found_name=false; + found_no_name=true; + break; + } + } + else { + strcpy(name, c->team_name); + memcpy(hash, c->team_hash, sizeof(hash)); + found_name=true; + } + } + else { + if(found_name) { + //Had a name, but this one has none + found_name=false; + found_no_name=true; + break; + } + found_no_name=true; + } + } + } + if(found_no_name || !found_one) { + if(named_team[team]) { + Packet_servernameteam p; + p.team=team; + p.name[0]=0; + net->dispatch(&p, P_SERVERNAMETEAM, game->loopback_connection); + set_team_name(team, p.name); + changed=true; + } + } + else { + if(!named_team[team] || strcmp(name, team_name[team])) { + Packet_servernameteam p; + p.team=team; + strcpy(p.name, name); + net->dispatch(&p, P_SERVERNAMETEAM, game->loopback_connection); + set_team_name(team, p.name); + changed=true; + } + } + } + if(changed) + notify_all(); +} + +bool Net_list::all_dead_or_gone() const { + int i; + for(i=0; iidle<2) + return false; + } + return true; +} + +bool Net_list::all_gone() const { + int i; + for(i=0; iidle!=3) + return false; + } + return true; +} + +void Net_list::syncto(Byte syncpoint) { + if(!game->server) + return; + if(this->syncpoint==syncpoint) + return; + msgbox("Net_list::syncto: syncing to %i\n", syncpoint); + this->syncpoint=syncpoint; + Packet_serverstate ps; + ps.state=syncpoint; + net->dispatch(&ps, P_SERVERSTATE, game->loopback_connection); +} + +Dword Net_list::gettimer() const { + return game->stats[GS::PLAYING_TIME].get_value(); +} + +bool Net_list::competitive() const { + Byte one_team=255; + Canvas *c; + for(int i=0; iidle < 2) { // Vivant + if(one_team==255) { + one_team=c->color; + } + else { + if(one_team!=c->color) { //Un autre vivant dans une autre team + return true; + } + } + } + } + } + return false; +} + +bool Net_list::would_be_competitive() const { + Byte one_team=255; + Canvas *c; + for(int i=0; iidle < 3) { // Not gone + if(one_team==255) { + one_team=c->color; + } + else { + if(one_team!=c->color) { //Un autre vivant dans une autre team + return true; + } + } + } + } + } + return false; +} + +void Net_list::pause_all() { + for(int i=0; iover->pause(); +} + +void Net_list::unpause_all() { + for(int i=0; iover->unpause(); +} + +void Net_list::check_pause() { + if(game->delay_start && game->delay_start != 500) { + game->delay_start--; + if(game->delay_start == 0) { + message(-1, ST_GAMESTARTNOW); + game->paused = false; + } + } +} + +void Net_list::check_drop() { + { + Packet_serverdropplayer *p=(Packet_serverdropplayer *) game->peekpacket(P_SERVERDROPPLAYER); + if(p) { + Canvas *canvas=get(p->player); + Drop_reason reason=p->reason; + game->removepacket(); + if(canvas) { + msgbox("Net_list::check_drop: server says player %i should go\n", canvas->num_player); + bool drop_it=false; + if(canvas->islocal()) + drop_it=true; + if(game->server && canvas->remote_adr==NULL) + drop_it=true; + if(game->server && canvas->idle==3) + drop_it=true; + if(drop_it) { + //The server wants me, poor helpless canvas, to drop + // dead. So, with neither shame nor glory, I'll + // unceremoniously send a P_CLIENTDROPPLAYER before + // killing my sorry self. + // Thus I beg you, fellow Net_clients all over the + // planet, to wait in anguish for the dreaded + // P_DROPPLAYER that will extinguish my pathetic + // little life. Ah! The tragedy! + //(or maybe the sucker is already gone so the server + // will speak in his stead) + Packet_clientdropplayer p; + p.player=canvas->num_player; + p.reason=reason; + net->sendtcp(&p); + Packet_dropplayer p2; + p2.player=canvas->num_player; + p2.reason=reason; + game->net_list.drop_player(&p2, true); + } + } + } + } + //Drop connections not answering the P_SERVERDROPPLAYER we sent + if(game->server) { + int i; + for(i=0; itrying_to_drop) { + //Force drop when disconnected + if(c->remote_adr==NULL || c->idle==3) { + Packet_dropplayer p; + p.player=i; + p.reason=DROP_MANUAL; + net->dispatch(&p, P_DROPPLAYER); + if(game->net_server) + game->net_server->record_packet(&p); + if(c->remote_adr) { + //This may have the effect of dropping other players + // on this connection. That's ok. + c->remote_adr->disconnect(); + } + } + } + } + } + + Packet_dropplayer *p; + while((p=(Packet_dropplayer *) game->peekpacket(P_DROPPLAYER))) { + drop_player(p, true); + game->removepacket(); + } +} + +void Net_list::check_gone() { + Packet_gone *p=(Packet_gone *) game->peekpacket(P_GONE); + if(p && !get(p->player)) { + msgbox("Net_list::check_gone: Received a P_GONE but Player %i has been dropped! Ignoring.\n", p->player); + game->removepacket(); + } +} + +void Net_list::check_stat() { + { + Packet_stat *p=(Packet_stat *) game->peekpacket(P_STAT); + if(p) { + Canvas *c=get(p->player); + if(c && !c->wait_download) { + if(!c->islocal()) + for(int i=0; inet_stats.size(); i++) { + Net_stat *ns=p->net_stats[i]; + *(c->stats[ns->st].get_address())=ns->value; + } + game->removepacket(); + } + } + } + { + Packet_gamestat *p=(Packet_gamestat *) game->peekpacket(P_GAMESTAT); + if(p) { + for(int i=0; i>p->net_stats.size(); i++) { + Net_stat *ns=p->net_stats[i]; + *(game->stats[ns->st].get_address())=ns->value; + } + game->removepacket(); + } + } +} + +void Net_list::server_drop_player(Byte player, Drop_reason reason) { + if(!game->server) + return; + msgbox("Net_list::server_drop_player: player==%i.\n", player); + Packet_serverdropplayer p; + p.player = player; + p.reason = reason; + Canvas *c=get(p.player); + if(c && !c->trying_to_drop) { + net->dispatch(&p, P_SERVERDROPPLAYER); + c->trying_to_drop=true; + } +} + +void Net_list::drop_player(Packet_dropplayer *p, bool chat) { + Canvas *c=get(p->player); + if(c==NULL) + return; + + if(chat) { + sprintf(st, ST_BOBWASDROP, c->name); + message(-1, st); + } + + char *reason="unknown"; + switch(p->reason) { + case DROP_AUTO: reason="auto"; break; + case DROP_MANUAL: reason="manual"; break; + case DROP_INVALID_BLOCK: reason="invalid_block"; break; + default: break; + } + log_step("player_drop\t%u\t%s", c->id(), reason); + /* + Can't do this crap: see comment in rejoin_player below. + + for(int i=0; iattacks[c->num_player] = 0; // flush son total + if(c2->last_attacker == c->num_player) + c2->last_attacker = 255; + } + } + */ + remove_player(c); + update_team_names(); +} + +void Net_list::rejoin_player(Canvas *c) { + /* + Can't do this crap: imagine if the computer where c2 is local + is dying right now, it will possibly add a frag to c but when + we (eventually) get the P_DEAD on the other clients, we won't + add a frag to the rejoined player because his attack count + would be zero... + + for(int i=0; iattacks[c->num_player] = 0; // flush son total + if(c2->last_attacker == c->num_player) + c2->last_attacker = 255; + } + } + */ + list[c->num_player] = NULL; + c->hide(); // ce canvas doit absolument disparaitre lorsqu'il y a rejoin + if(!game->server) // si serveur, remote_adr doit etre la net_connection du nouveau proprietaire de ce canvas + c->remote_adr = net->server_addr(); + // attention: Dans le cas d'un rejoin, le canvas n'est pas deleter, mais on + // met NULL dans l'array pour indiquer aux watcheurs de fermer leur fenetre pour ce joueur + notify_all(); + list[c->num_player] = c; + //Now tell them about this "new" player + notify_all(); +} + +int Net_list::size() { + int ret=0; + int i; + for(i=0; ipeekpacket(P_PLAYER); + if(!p) + return; + Canvas *c; + if(game->server) { + c=get(p->pos); + } + else { + c=new Canvas(game->seed, p->team, p->name, p->h_repeat, p->v_repeat, p->smooth? true:false, p->shadow? true:false, p->handicap, net->server_addr(), 0, false); + c->set_id(p->player_id); + set_player(c, p->pos, true); + } + game->removepacket(); +} + +void Net_list::check_admin() { + if(!game->server) + return; + int co; + for(co=0; coconnections.size(); co++) { + Net_connection *nc=net->connections[co]; + if(!nc->packet_based && nc->incoming->size()) { + Byte *buf=nc->incoming->get(); + Dword size=nc->incoming->size(); + char line[256]; + unsigned int line_size = 0; + bool got_line=false; + while(line_size < 255U && line_size < size) { + if(*buf=='\r' || *buf=='\n') { + got_line=true; + break; + } + line[line_size]=*buf++; + if(line[line_size]>0 && line[line_size]<' ') + line[line_size]=' '; + line_size++; + } + if(got_line) { + line[line_size]=0; + //Skip any amount of CR/LF + while(line_size < size && (*buf=='\r' || *buf=='\n')) { + line_size++; + buf++; + } + nc->incoming->remove_from_start(line_size); + got_admin_line(line, nc); + } + } + } +} + +bool Net_list::accept_connection(Net_connection *nc) { + if(nc==game->loopback_connection) + return true; + bool ret=true; + int i; + for(i=0; iaddress()); + if(*ip>=c) { + ret=false; + break; + } + } + for(i=0; iaddress()); + if(*ip>=c) { + ret=true; + break; + } + } + return ret; +} + +void Net_list::client_deconnect(Net_connection *nc) { + int i; + //Clean up command cache + for(i=0; inc == nc) { + //Remove cache entry + delete cmd_cache[i]; + cmd_cache.remove(i); + i--; + } + } + //Clean up watchers + for(i=0; iremove_watcher(nc); + } +} + +void Net_list::got_admin_line(const char *line, Net_connection *nc) { + if(!game || !game->server) + return; + if(!*line) + return; + if(line && nc) { + char st[64]; + net->stringaddress(st, nc->address(), nc->getdestport()); + msgbox("Net_list::got_admin_line: %s from %s\n", line, st); + } + if(*line!='/' && nc) { + char st[1024]; + sprintf(st, "%u: %s", nc->id(), line); + log_step("chat\t%u\t%s", nc->id(), st); + message(-1, st, true, false, false, nc); + return; + } + //process server commands here + char cmd[256]; + char params[256]; + strcpy(cmd, line+1); //Skip '/' + char *sp=strchr(cmd, ' '); + if(sp) { + *sp=0; + strcpy(params, sp+1); + } + else { + params[0]=0; + } + int i; + if(!cmd[0]) { + //Repeat last command + for(i=0; inc == nc) { + strcpy(cmd, cmd_cache[i]->cmd); + if(!params[0]) + strcpy(params, cmd_cache[i]->params); + } + } + if(nc && strcmp(cmd, "admin") && strcmp(cmd, "setpasswd")) { + //Update cache if real connection and command not admin or + // setpasswd + for(i=0; inc == nc) { + //Update existing cache entry + cmd_cache[i]->set(cmd, params); + break; + } + } + if(i == cmd_cache.size()) { + //Not found, create new cache entry + cmd_cache.add(new Lastline(nc, cmd, params)); + } + } + bool trusted=false; + if(!nc) + trusted=true; + if(nc && nc->trusted) + trusted=true; + if(!strcmp(cmd, "help")) { + send_msg(nc, "/help This help text."); + send_msg(nc, "/admin Turn on administrator mode."); + send_msg(nc, "/setpasswd [password] Set admin password or disable remote admin."); + send_msg(nc, "/list List client connections."); + send_msg(nc, "/drop Drop a player or connection."); + send_msg(nc, "/allowstart [0|1] (Dis)allow anybody to start the game."); + send_msg(nc, "/allowpause [0|1] (Dis)allow anybody to pause/unpause the game."); + send_msg(nc, "/pause Pause/unpause the game."); + send_msg(nc, "/acceptplayers [0|1] (Dis)allow new players to join game."); + send_msg(nc, "/acceptconnects [0|1] (Dis)allow new client computers to join game."); + send_msg(nc, "/laglimit [limit] Display/change lag limit."); + send_msg(nc, "/ppmlimit [limit] Display/change maximum ppm."); + send_msg(nc, "/autorestart [0|1] Disable/enable auto-restart at game end."); + send_msg(nc, "/autodrop [seconds] Set or disable (0) auto-drop time."); + send_msg(nc, "/public [0|1] Make game invisible (0) or public (1)."); + send_msg(nc, "/name [name] Display/change game name."); + send_msg(nc, "/motd [message] Display/change message of the day."); + send_msg(nc, "/endgame End current game."); + send_msg(nc, "/quit End game and shut down server."); + send_msg(nc, "/score Scoreboard."); + send_msg(nc, "/version Display Quadra version and build."); + send_msg(nc, "/bye Terminate session."); + } + if(!strcmp(cmd, "admin")) + if(admin_password[0] && !strcmp(params, admin_password) && nc) { + if(nc->trusted) { + send_msg(nc, "Already administrator"); + } + else { + char st[32], st1[256]; + Net::stringaddress(st1, nc->address()); + sprintf(st, "%s:%i", st1, nc->getdestport()); + sprintf(st1, "Granting admin privileges to %s", st); + message(-1, st1, true, false, true); + nc->trusted=true; + send_msg(nc, "Admin commands available"); + } + } + if(!strcmp(cmd, "score")) { + if(!size()) + send_msg(nc, "No players in game"); + else { + Byte team, o; + for(o=0; o=1) { + char st[64]; + strcpy(st, english_teams[team]); + strcat(st, " team:"); + send_msg(nc, "%45.45s", st); + } + Dword frag, death; + if(score.player_count[team]) { + for(i=0; icolor==team) { + frag=score.stats[player].stats[CS::FRAG].get_value(); + death=score.stats[player].stats[CS::DEATH].get_value(); + send_msg(nc, "%45.45s %4i %4i", c->long_name(true, false), frag, death); + } + } + } + if(score.player_count[team]>=1) { + frag=score.team_stats[team].stats[CS::FRAG].get_value(); + death=score.team_stats[team].stats[CS::DEATH].get_value(); + send_msg(nc, "%45.45s %4i %4i", "Total:", frag, death); + } + } + } + } + if(!strcmp(cmd, "version")) { + send_msg(nc, "Quadra %i.%i.%i", Config::major, Config::minor, Config::patchlevel); + send_msg(nc, "%s", built); + } + if(!strcmp(cmd, "bye") && nc) { + if(!(game && game->loopback_connection==nc)) { + send_msg(nc, "See ya"); + nc->disconnect(); + } + } + if(!strcmp(cmd, "motd")) { + if(params[0] && trusted) { + if(!strcmp(params, "-")) { + motd[0]=0; + send_msg(nc, "Message of the day deleted"); + } + else { + strncpy(motd, params, sizeof(motd)); + motd[sizeof(motd)-1]=0; + send_msg(nc, "Message of the day changed"); + } + } + else + if(motd[0]) + send_msg(nc, "%s", motd); + else + send_msg(nc, "No message of the day defined"); + } + if(!strcmp(cmd, "setpasswd") && trusted) { + strncpy(admin_password, params, sizeof(admin_password)-1); + admin_password[sizeof(admin_password)-1]=0; + if(admin_password[0]) + send_msg(nc, "Password changed"); + else + send_msg(nc, "Remote administration disabled"); + } + if(!strcmp(cmd, "name")) { + if(params[0] && trusted) { + strncpy(game->name, params, sizeof(game->name)-1); + game->name[sizeof(game->name)-1]=0; + } + send_msg(nc, "Game name: %s", game->name); + } + if(!strcmp(cmd, "endgame") && trusted) { + send_end_signal(false); + send_msg(nc, "End signaled"); + } + if(!strcmp(cmd, "list") && trusted) { + //-1 so we don't count loopback connection + send_msg(nc, "Total connections: %i", net->connections.size()-1); + for(i=0; iconnections.size(); i++) { + Net_connection *nc2=net->connections[i]; + if(nc2) { + char st[256]; + if(nc2 == game->loopback_connection) + continue; // skip l'adresse local + Net::stringaddress(st, nc2->address()); + { + char st2[16]; + sprintf(st2, ":%i", nc2->getdestport()); + strcat(st, st2); + } + if(nc2->trusted) + strcat(st, " *"); + bool got_one=false; + int p; + for(p=0; premote_adr==nc2) { + if(got_one) + strcat(st, ", "); + else { + got_one=true; + strcat(st, ": "); + } + strcat(st, "["); + strcat(st, c->name); + if(c->idle==3) + strcat(st, " *"); + strcat(st, "]"); + } + } + send_msg(nc, "%s", st); + } + } + } + if(!strcmp(cmd, "drop") && trusted) { + bool dropped=false; + //Look for a connection to drop + char addr[256]; + int port; + strcpy(addr, params); + char *col=strchr(addr, ':'); + if(col) { + *col=0; //Cut address at ':' + port=atoi(col+1); + if(!strcmp(col+1, "*")) + port=-1; + } + else { + //If port wasn't specified, we'll drop every connection + // originating from that addr + port=-1; + } + Dword ad=Net::dotted2addr(addr); + if(ad!=INADDR_NONE) { + for(i=0; iconnections.size(); i++) { + Net_connection *nc2=net->connections[i]; + if(game && nc2==game->loopback_connection) + continue; //Don't ever drop loopback connection + if(nc2->address()==ad) { + if(nc2->getdestport()==port || port==-1) { + char ad[16]; + Net::stringaddress(ad, nc2->address()); + Word po=nc2->getdestport(); + nc2->disconnect(); + send_msg(nc, "Dropping connection %s:%i", ad, po); + dropped=true; + } + } + } + } + if(!dropped) { + //Haven't found a connection to drop, look for a player + for(i=0; iname, params)) { + send_msg(nc, "Dropping player %s", c->name); + server_drop_player(i, DROP_MANUAL); + dropped=true; + } + } + } + if(!dropped) + send_msg(nc, "No match"); + } + if(!strcmp(cmd, "acceptplayers")) { + if(params[0] && trusted) { + i=atoi(params); + game->server_accept_player=i? 0:1; + } + send_msg(nc, "Accept player: %s", game->server_accept_player? "off":"on"); + } + if(!strcmp(cmd, "acceptconnects")) { + if(params[0] && trusted) { + i=atoi(params); + game->server_accept_connection=i? 0:1; + } + send_msg(nc, "Accept connection: %s", game->server_accept_connection? "off":"on"); + } + if(!strcmp(cmd, "ppmlimit")) { + if(params[0] && trusted) { + Dword i=atoi(params); + ppm_limit=i; + } + if(ppm_limit) + send_msg(nc, "PPM limit: %u", ppm_limit); + else + send_msg(nc, "PPM limit: disabled"); + } + if(!strcmp(cmd, "laglimit")) { + if(params[0] && trusted) { + Dword i=atoi(params); + lag_limit=i; + } + if(lag_limit) + send_msg(nc, "Lag limit: %u", lag_limit); + else + send_msg(nc, "Lag limit: disabled"); + } + if(!strcmp(cmd, "autorestart")) { + if(params[0] && trusted) { + i=atoi(params); + game->auto_restart=i? true:false; + } + send_msg(nc, "Auto restart: %s", game->auto_restart? "on":"off"); + } + if(!strcmp(cmd, "public")) { + if(params[0] && trusted) { + i=atoi(params); + game->game_public=i? true:false; + } + send_msg(nc, "Public game: %s", game->game_public? "on":"off"); + } + if(!strcmp(cmd, "autodrop")) { + if(params[0] && trusted) { + float i=atof(params); + gone_time_limit=(Dword) (i*100.0); + } + if(gone_time_limit) + send_msg(nc, "Auto drop time: %.2f seconds", gone_time_limit/100.0); + else + send_msg(nc, "Auto drop: disabled"); + } + if(!strcmp(cmd, "allowstart")) { + if(params[0] && trusted) { + i=atoi(params); + game->net_server->allow_start=i? true:false; + } + send_msg(nc, "Allow start: %s", game->net_server->allow_start? "on":"off"); + } + if(!strcmp(cmd, "allowpause")) { + if(params[0] && trusted) { + i=atoi(params); + game->net_server->allow_pause=i? true:false; + } + send_msg(nc, "Allow pause: %s", game->net_server->allow_pause? "on":"off"); + } + if(!strcmp(cmd, "pause")) { + Packet_clientpause p; + p.from=nc; + game->net_server->clientpause(&p); + } + if(!strcmp(cmd, "quit") && trusted) { + quit_fast(); + send_end_signal(false); + send_msg(nc, "Shutting down server"); + } + if(!strcmp(cmd, "stack") && trusted) { + send_msg(nc, "Dumping game stack. overmind framecount is %i:", overmind.framecount); + send_msg(nc, "stack size=%i:", game->stack.size()); + for(int j=0; jstack.size(); j++) { + Packet *p = game->stack[j]; + if(net && net->net_param) { + char st[4096]; + net->net_param->print_packet(p, st); + send_msg(nc, "%s", st); + } + } + } + if(!strcmp(cmd, "in") && trusted) { + int i, c=0; + for(i=0; iconnections.size(); i++) { + Net_connection *nc2=net->connections[i]; + if(nc2 && nc2!=game->loopback_connection && nc2->packet_based && nc2->joined) { + char st[64]; + net->stringaddress(st, nc2->address(), nc2->getdestport()); + send_msg(nc, "%s: %i", st, nc2->incoming_inactive); + c++; + } + } + send_msg(nc, "Total: %i", c); + } + if(!strcmp(cmd, "netstat") && trusted) { + int i; + send_msg(nc, "Incoming (min, max, nz-avg):"); + for(i=0; iconnections.size(); i++) { + Net_connection *nc2=net->connections[i]; + if(nc2 && nc2!=game->loopback_connection) { + char st[64]; + net->stringaddress(st, nc2->address(), nc2->getdestport()); + send_msg(nc, "%21.21s: %u %u %.2f", st, nc2->incoming_min, nc2->incoming_max, (float)nc2->incoming_total/nc2->commit_count_in); + } + } + send_msg(nc, ""); + send_msg(nc, "Outgoing (min, max, nz-avg):"); + for(i=0; iconnections.size(); i++) { + Net_connection *nc2=net->connections[i]; + if(nc2 && nc2!=game->loopback_connection) { + char st[64]; + net->stringaddress(st, nc2->address(), nc2->getdestport()); + send_msg(nc, "%21.21s: %u %u %.2f", st, nc2->outgoing_min, nc2->outgoing_max, (float)nc2->outgoing_total/nc2->commit_count_out); + } + } + } + bool allow=!strcmp(cmd, "allow"); + bool deny=!strcmp(cmd, "deny"); + if((allow || deny) && trusted) { + int i; + char st[64]; + if(params[0]) { + IP_addr ad(params); + if(ad.ip || ad.mask) { + IP_addr *ip; + bool would_be_denied=false; + for(i=0; i*ip) { + if(allow) { + ip->print(st); + send_msg(nc, "%15.15s no longer denied", st); + } + delete ip; + deny_list.remove(i); + i--; + } + if(*ip>=ad) { + would_be_denied=true; + if(deny && *ip>=ad) { + deny=false; + break; + } + } + } + for(i=0; i*ip) { + if(deny) { + ip->print(st); + send_msg(nc, "%15.15s no longer allowed", st); + } + delete ip; + allow_list.remove(i); + i--; + } + if(allow && *ip>=ad) { + allow=false; + break; + } + } + if(deny) { + ip=new IP_addr(ad); + deny_list.add(ip); + ip->print(st); + send_msg(nc, "%15.15s denied", st); + } + if(allow && would_be_denied) { + ip=new IP_addr(ad); + allow_list.add(ip); + ip->print(st); + send_msg(nc, "%15.15s allowed", st); + } + } + } + else { + send_msg(nc, "Deny list:"); + for(i=0; iprint(st); + send_msg(nc, "%15.15s", st); + } + send_msg(nc, "Total: %i", i); + send_msg(nc, "Allow list:"); + for(i=0; iprint(st); + send_msg(nc, "%15.15s", st); + } + send_msg(nc, "Total: %i", i); + } + } +} + +Net_list_stepper::Net_list_stepper(Net_list *nl) { + the_net_list=nl; +} + +void Net_list_stepper::step() { + the_net_list->step_all(); +} diff --git a/source/net_server.cpp b/source/net_server.cpp new file mode 100644 index 0000000..1aadb69 --- /dev/null +++ b/source/net_server.cpp @@ -0,0 +1,695 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "game.h" +#include "canvas.h" +#include "chat_text.h" +#include "texte.h" +#include "global.h" +#include "config.h" +#include "sons.h" +#include "recording.h" +#include "nglog.h" +#include "net_server.h" + +Net_client::Net_client() { + msgbox("Net_client::Net_client\n"); + net->addwatch(P_GAMESTAT, this); + net->addwatch(P_SERVERNAMETEAM, this); + net->addwatch(P_MOVES, this); + net->addwatch(P_DROPPLAYER, this); + net->addwatch(P_SERVERDROPPLAYER, this); + net->addwatch(P_STAT, this); + net->addwatch(P_PAUSE, this); + net->addwatch(P_PLAYER, this); + net->addwatch(P_STAMPBLOCK, this); + net->addwatch(P_DEAD, this); + net->addwatch(P_RESPAWN, this); + net->addwatch(P_LINES, this); + net->addwatch(P_FIRST_FRAG, this); + net->addwatch(P_GONE, this); + net->addwatch(P_ENDGAME, this); + net->addwatch(P_REJOIN, this); + net->addwatch(P_SERVERSTATE, this); + net->addwatch(P_STATE, this); + net->addwatch(P_SERVERRANDOM, this); + net->addwatch(P_SERVERPOTATO, this); + net->addwatch(P_SERVERTESTPING, this); + net->addwatch(P_REMOVEBONUS, this); +} + +Net_client::~Net_client() { + net->removewatch(P_REMOVEBONUS, this); + net->removewatch(P_SERVERTESTPING, this); + net->removewatch(P_SERVERPOTATO, this); + net->removewatch(P_SERVERRANDOM, this); + net->removewatch(P_STATE, this); + net->removewatch(P_SERVERSTATE, this); + net->removewatch(P_REJOIN, this); + net->removewatch(P_ENDGAME, this); + net->removewatch(P_GONE, this); + net->removewatch(P_FIRST_FRAG, this); + net->removewatch(P_LINES, this); + net->removewatch(P_RESPAWN, this); + net->removewatch(P_DEAD, this); + net->removewatch(P_STAMPBLOCK, this); + net->removewatch(P_PLAYER, this); + net->removewatch(P_PAUSE, this); + net->removewatch(P_STAT, this); + net->removewatch(P_SERVERDROPPLAYER, this); + net->removewatch(P_DROPPLAYER, this); + net->removewatch(P_MOVES, this); + net->removewatch(P_SERVERNAMETEAM, this); + net->removewatch(P_GAMESTAT, this); + //Intricate, isn't it? :) + if(game->single) + net->stop_client(); +} + +void Net_client::net_call(Packet *p2) { + switch(p2->packet_id) { + case P_SERVERTESTPING: + p2->packet_id=P_TESTPING; + net->sendtcp(p2); + break; + case P_PAUSE: + pause(p2); + break; + case P_STAT: + case P_DROPPLAYER: + case P_GONE: + case P_DEAD: + case P_MOVES: + case P_REMOVEBONUS: + case P_SERVERDROPPLAYER: + case P_PLAYER: + case P_STAMPBLOCK: + case P_RESPAWN: + case P_LINES: + case P_FIRST_FRAG: + case P_REJOIN: + case P_SERVERSTATE: + case P_STATE: + case P_SERVERPOTATO: + case P_ENDGAME: + case P_SERVERRANDOM: + case P_SERVERNAMETEAM: + case P_GAMESTAT: + game->stackpacket(p2); + return; + } + delete p2; +} + +void Net_client::pause(Packet *p2) { + msgbox("Net_client::pause\n"); + if(game->paused) { + if(game->delay_start==500) { + message(-1, ST_GAMEWILLSTART); + game->delay_start = 499; // debut le compte a rebours + msgbox("Net_client::pause: starting countdown...\n"); + log_step("game_start"); + return; // n'enleve pas game->paused tout de suite + } + if(game->delay_start != 0) { + game->delay_start = 0; // force l'arret du compte a rebours s'il etait actif + msgbox("Net_client::pause: stop countdown...\n"); + return; // et reste sur pause + } + message(-1, "Game unpaused", true, true); + if(p2 && p2->from) + log_step("unpause\t%u", p2->from->id()); + } else { + Packet_pause *p=(Packet_pause *) p2; + const char *pn; + if(p->player == -1) + pn = ST_SERVER; + else { + Canvas *c=game->net_list.get(p->player); + if(c) + pn = c->name; + else + pn = ST_SERVER; + } + sprintf(st, ST_PAUSEDBYBOB, pn); + message(-1, st); + if(p2 && p2->from) + log_step("pause\t%u", p2->from->id()); + } + game->paused=!game->paused; + msgbox("Net_client::pause done\n"); +} + +Net_server::Net_server() { + allow_start=true; + allow_pause=true; + net->start_server(game->network); + net->addwatch(P_CLIENTPAUSE, this); + net->addwatch(P_WANTJOIN, this); + net->addwatch(P_FINDGAME, this); + net->addwatch(P_CLIENTCHAT, this); + net->addwatch(P_PLAYERWANTJOIN, this); + net->addwatch(P_CLIENTSTAMPBLOCK, this); + net->addwatch(P_CLIENTDEAD, this); + net->addwatch(P_CLIENTRESPAWN, this); + net->addwatch(P_CLIENTSTARTWATCH, this); + net->addwatch(P_CLIENTTESTPING, this); + net->addwatch(P_CLIENTLINES, this); + net->addwatch(P_CLIENTFIRST_FRAG, this); + net->addwatch(P_CLIENTGONE, this); + net->addwatch(P_CLIENTMOVES, this); + net->addwatch(P_CLIENTSTATE, this); + net->addwatch(P_CLIENTDROPPLAYER, this); + net->addwatch(P_BYE, this); + net->addwatch(P_CLIENTREMOVEBONUS, this); +} + +Net_server::~Net_server() { + net->removewatch(P_CLIENTREMOVEBONUS, this); + net->removewatch(P_BYE, this); + net->removewatch(P_CLIENTDROPPLAYER, this); + net->removewatch(P_CLIENTSTATE, this); + net->removewatch(P_CLIENTMOVES, this); + net->removewatch(P_CLIENTGONE, this); + net->removewatch(P_CLIENTFIRST_FRAG, this); + net->removewatch(P_CLIENTLINES, this); + net->removewatch(P_CLIENTTESTPING, this); + net->removewatch(P_CLIENTSTARTWATCH, this); + net->removewatch(P_CLIENTRESPAWN, this); + net->removewatch(P_CLIENTDEAD, this); + net->removewatch(P_CLIENTSTAMPBLOCK, this); + net->removewatch(P_PLAYERWANTJOIN, this); + net->removewatch(P_CLIENTCHAT, this); + net->removewatch(P_FINDGAME, this); + net->removewatch(P_WANTJOIN, this); + net->removewatch(P_CLIENTPAUSE, this); + net->stop_server(); +} + +void Net_server::record_packet(Packet *p2) { + if(recording) + recording->write_packet(p2); +} + +void Net_server::stop_multi_recording() { + if(recording) { + delete recording; + recording=NULL; + } +} + +void Net_server::net_call(Packet *p2) { + Packet_playerbase *ppb=NULL; + switch(p2->packet_id) { + case P_CLIENTDROPPLAYER: + case P_CLIENTSTAMPBLOCK: + case P_CLIENTDEAD: + case P_CLIENTRESPAWN: + case P_CLIENTLINES: + case P_CLIENTFIRST_FRAG: + case P_CLIENTGONE: + case P_CLIENTMOVES: + case P_CLIENTSTATE: + case P_CLIENTREMOVEBONUS: + ppb=(Packet_playerbase *) p2; + } + if(ppb) { + Canvas *c=game->net_list.get(ppb->player); + if(!c) { + if(ppb->packet_id==P_CLIENTDROPPLAYER && ppb->from==game->loopback_connection) { + //We let through a P_CLIENTDROPPLAYER about a dropped + // player if it's coming from the loopback_connection + // cause the canvas is already deleted in that case. + } + else { + msgbox("Net_server::net_call: got a Packet_playerbase but player has been dropped, ignoring.\n"); + delete p2; + return; + } + } + //Check whether the originating connection of + // the packet is consistent with the player for cheat + // protection. For some reason, the remote_adr of canvases + // on the loopback client is NULL so we'll automatically + // trust the loopback_connection + if(c && c->remote_adr!=p2->from && p2->from!=game->loopback_connection) { + msgbox("Net_server::net_call: got a Packet_playerbase from wrong connection, ignoring.\n"); + msgbox(" id==%i, player==%i, remote_adr==%s%p, from==%p.\n", p2->packet_id, ppb->player, c? "":"[c is NULL] ", c? c->remote_adr:NULL, p2->from); + delete p2; + return; + } + } + if(p2->from && !p2->from->joined && p2->packet_id!=P_WANTJOIN && p2->packet_id!=P_CLIENTCHAT) { + //Until joined, we accept only P_CLIENTCHAT or P_WANTJOIN + //Anything else is ignored + delete p2; + return; + } + switch(p2->packet_id) { + case P_PLAYERWANTJOIN: + playerwantjoin(p2); + break; + case P_FINDGAME: + findgame(p2); + break; + case P_CLIENTCHAT: + clientchat(p2); + break; + case P_CLIENTPAUSE: + clientpause(p2); + break; + case P_CLIENTDROPPLAYER: + net->dispatch(p2, P_DROPPLAYER, p2->from); + record_packet(p2); + break; + case P_CLIENTSTAMPBLOCK: + net->dispatch(p2, P_STAMPBLOCK, p2->from); + record_packet(p2); + break; + case P_CLIENTDEAD: + net->dispatch(p2, P_DEAD, p2->from); + record_packet(p2); + break; + case P_CLIENTRESPAWN: + net->dispatch(p2, P_RESPAWN, p2->from); + record_packet(p2); + break; + case P_CLIENTSTARTWATCH: + clientstartwatch(p2); + break; + case P_WANTJOIN: + wantjoin(p2); + return; + case P_CLIENTTESTPING: + p2->packet_id = P_TESTPING; + net->sendtcp(p2->from, p2); + break; + case P_CLIENTLINES: + net->dispatch(p2, P_LINES, p2->from); + record_packet(p2); + break; + case P_CLIENTFIRST_FRAG: + net->dispatch(p2, P_FIRST_FRAG, p2->from); + record_packet(p2); + break; + case P_CLIENTGONE: + net->dispatch(p2, P_GONE, p2->from); + record_packet(p2); + break; + case P_CLIENTMOVES: + p2->packet_id=P_MOVES; + record_packet(p2); + clientmoves(p2); + break; + case P_CLIENTSTATE: + net->dispatch(p2, P_STATE, p2->from); + record_packet(p2); + break; + case P_CLIENTREMOVEBONUS: + net->dispatch(p2, P_REMOVEBONUS, p2->from); + record_packet(p2); + break; + case P_BYE: + p2->from->disconnect(); + break; + } + delete p2; +} + +void Net_server::playerwantjoin(Packet *p2) { + Packet_playerwantjoin *p=(Packet_playerwantjoin *) p2; + msgbox("Net_server::playerwantjoin, name=%s\n", p->name); + Packet_playeraccepted playeraccepted; + playeraccepted.accepted = game->server_accept_player; + playeraccepted.pos=0; + if(game->terminated) + playeraccepted.accepted = 3; // la partie est terminee: accepte aucun joueur + if(playeraccepted.accepted == 0) { + for(int i=0; inet_list.get(i); + if(c) { + if(!strcmp(c->name, p->name) && !memcmp(c->player_hash, p->player_hash, sizeof(c->player_hash))) { // si deja qqun avec ce nom la + if(c->idle == 3) { // si joueur 'gone' + if(c->color != p->team) { + // si join avec une team differente, drop-le et accepte le nouveau + Packet_dropplayer p3; + p3.player = i; + p3.reason = DROP_AUTO; + net->dispatch(&p3, P_DROPPLAYER, game->loopback_connection); + record_packet(&p3); + game->net_list.drop_player(&p3, true); // drop immediatement le joueur + } else { // si meme team, conserve les stats + playeraccepted.accepted = 4; // deja qqun: on va le remplacer ce mec! + playeraccepted.pos = i; // indique le numero du joueur a remplacer + playeraccepted.answer(p); + + Packet_rejoin p_rejoin; + p_rejoin.player = i; + p_rejoin.h_repeat = p->h_repeat; + p_rejoin.v_repeat = p->v_repeat; + p_rejoin.smooth = p->smooth; + p_rejoin.shadow = p->shadow; + p_rejoin.handicap = p->handicap; + net->dispatch(&p_rejoin, P_REJOIN); + record_packet(&p_rejoin); + c->remote_adr = p->from; // le serveur re-ajuste la remote_adr du canvas + Dword id=0; + if(p->from!=game->loopback_connection) + id=p->from->id(); + log_step("player_rejoin\t%u\t%u\t%s", c->id(), id, log_handicap(p->handicap)); + game->net_list.update_team_names(); + return; + } + } else { + playeraccepted.accepted = 2; // deja qqun: refuse le joueur + } + break; + } + } + } + if(playeraccepted.accepted == 0) { + if(game->net_list.size() == MAXPLAYERS) + playeraccepted.accepted = 5; // game is full, can't join + } + } + + if(playeraccepted.accepted == 0) { // si on accepte le joueur + Packet_player player; // dispatch P_PLAYER a tous (sauf le cok demandant!!). + strcpy(player.name, p->name); + player.player = p->player; + player.team = p->team; + Canvas *canvas=new Canvas(game->seed, p->team, p->name, p->h_repeat, p->v_repeat, p->smooth? true:false, p->shadow? true:false, p->handicap, p->from, p->player, false); + strncpy(canvas->team_name, p->team_name, 40); + canvas->team_name[39]=0; + playeraccepted.pos=game->net_list.add_player(canvas); + player.pos=playeraccepted.pos; + player.h_repeat = p->h_repeat; + player.v_repeat = p->v_repeat; + player.smooth = p->smooth; + player.shadow = p->shadow; + player.handicap = p->handicap; + net->dispatch(&player, P_PLAYER, p->from); + record_packet(&player); + Dword id=0; + if(p->from && p->from!=game->loopback_connection) + id=p->from->id(); + log_step("player_join\t%u\t%u\t%s\t%s\t%s\t%s", canvas->id(), id, log_team(p->team), log_handicap(p->handicap), canvas->name, canvas->team_name); + } + playeraccepted.answer(p); + game->net_list.update_team_names(); +} + +void Net_server::findgame(Packet *p2) { + Packet_gameinfo resp; + strcpy(resp.name, game->name); + resp.version = game->net_version(); + resp.port = config.info.port_number; + for(int i=0; inet_list.get(i); + if(c) + resp.add_player(i, c->color, c->name, c->idle, c->handicap); + } + resp.game_end_value = game->game_end_value; + resp.nolevel_up = !game->level_up; + resp.delay_start = game->delay_start ? true:false; + resp.terminated = game->terminated; + resp.level_start = game->level_start; + resp.combo_min = game->combo_min; + resp.allow_handicap = game->allow_handicap; + resp.survivor = game->survivor; + resp.hot_potato = game->hot_potato; + resp.normal_attack = game->normal_attack; + resp.clean_attack = game->clean_attack; + resp.potato_normal_attack = game->potato_normal_attack; + resp.potato_clean_attack = game->potato_clean_attack; + resp.game_end = game->game_end; + + //net->sendudp(INADDR_BROADCAST, &resp); + net->sendudp(p2->from_addr, &resp); +} + +void Net_server::wantjoin(Packet *p2) { + Packet_wantjoin *p=(Packet_wantjoin *) p2; + Executor *exec = new Executor(true); + pendings.add(exec); + exec->add(new Net_pendingjoin(p)); + overmind.start(exec); +} + +void Net_server::clientpause(Packet *p2) { + Packet_pause p; + msgbox("Net_server::clientpause from %x\n", p2->from); + if(game->delay_start && game->delay_start!=500) { + //Don't ever interrupt countdown + return; + } + bool allowed=false; + if(!p2->from || p2->from->trusted) + allowed=true; + if(game->delay_start==500 && allow_start) + allowed=true; + if(game->delay_start==0 && allow_pause) + allowed=true; + if(!allowed) + return; + if(game->paused) { + p.player=-1; + net->dispatch(&p, P_PAUSE); + record_packet(&p); + } + else { + int i; + for(i=0; inet_list.get(i)) { + if(game->net_list.get(i)->remote_adr == p2->from) + break; + } + } + if(i==MAXPLAYERS) + i=-1; + p.player=(signed char) i; + net->dispatch(&p, P_PAUSE); + record_packet(&p); + } +} + +void Net_server::clientstartwatch(Packet *p2) { + Packet_clientstartwatch *p=(Packet_clientstartwatch *) p2; + Canvas *c = game->net_list.get(p->player); + if(c == NULL) { + msgbox("Server: Packet_clientstartwatch by %x for player %i, but player is down. Ignoring\n", p->from, p->player); + return; + } + if(p->stop) { + msgbox("client: stop_watch for player %i\n", p->player); + c->remove_watcher(p->from); + } + else { + msgbox("client: start_watch for player %i\n", p->player); + c->add_watcher(new Canvas::Watcher(p->from)); + } +} + +void Net_server::clientmoves(Packet *p2) { + Packet_moves *p=(Packet_moves *) p2; + if(p->size<25) + return; + Canvas *c=game->net_list.get(p->player); + if(!c) + return; + int i; + for(i=0; iconnections.size(); i++) { + Net_connection *nc=net->connections[i]; + if(nc) + for(int j=0; jwatchers.size(); j++) { + Canvas::Watcher *w=c->watchers[j]; + if(w && w->nc==nc) + net->sendtcp(nc, p); + } + } +} + +void Net_server::clientchat(Packet *p2) { + Packet_clientchat *p=(Packet_clientchat *) p2; + if(p->text[0]=='/' && p->team==-1) { + if(game) + game->net_list.got_admin_line(p->text, p->from); + } + else { + net->dispatch(p, P_CHAT); + record_packet(p); + if(p && p->from) + log_step("chat\t%u\t%s", p->from!=game->loopback_connection? p->from->id():0, p->text); + } +} + +Net_pendingjoin::Net_pendingjoin(Packet_wantjoin *p) { + msgbox("Pending join for %x...\n", p->from); + pac = p; + net->add_watch(this); + cancel=false; +} + +Net_pendingjoin::~Net_pendingjoin() { + delete pac; + net->remove_watch(this); +} + +void Net_pendingjoin::load_packet_gameserver(Packet_gameserver* resp) { + resp->version = game->net_version(); + resp->accepted = 1; // 'accepted' est inutile depuis Quadra_param->accept_connection() + resp->game_seed = game->seed; + resp->paused = game->paused; + strcpy(resp->name, game->name); + resp->nolevel_up = !game->level_up; + resp->level_start = game->level_start; + resp->survivor = game->survivor; + resp->hot_potato = game->hot_potato; + resp->normal_attack = game->normal_attack; + resp->clean_attack = game->clean_attack; + resp->potato_normal_attack = game->potato_normal_attack; + resp->potato_clean_attack = game->potato_clean_attack; + resp->combo_min = game->combo_min; + resp->allow_handicap = game->allow_handicap; + resp->delay_start = game->delay_start; + resp->game_end = game->game_end; + resp->game_end_value = game->game_end_value; + if(game->game_end == 2) { // si game_end == temps en minute + Dword timer = game->net_list.gettimer(); + if(timer < (Dword)resp->game_end_value) + resp->game_end_value -= timer; // calcul et donne le temps restant seulement! + else + resp->game_end_value=0; + } + int i; + for(i=0; inet_list.get(i); + if(c) + resp->add_player(i, c->color, c->name, c->id(), c->handicap); + } + resp->wants_moves = game->wants_moves; + resp->syncpoint = game->net_list.syncpoint; + resp->potato_team = game->potato_team; + resp->single = game->single; + resp->terminated = game->terminated; +} + +void Net_pendingjoin::step() { + if((overmind.framecount+15)&127) + return; + int i, j, x, y; + msgbox("Pending join being processed for %x...\n", pac->from); + + if(cancel) { + msgbox(" Client disconnected, cancelling the whole deal\n"); + ret(); + return; + } + if(!game) { + msgbox(" No game, cancelling the whole deal\n"); + ret(); + return; + } + if(game->peekpacket(255)) { + msgbox(" Something on the stack!\n"); + return; + } + if(game->net_list.syncpoint!=Canvas::LAST) { + msgbox(" Currently syncing!\n"); + return; + } + for(i=0; inet_list.get(i); + if(c && (!c->idle || c->dying)) { + // si un canvas est pas 'idle' (process_key), abandon + msgbox(" Canvas %x is not idle now!\n", c); + return; + } + } + msgbox(" Uploading canvas information!\n"); + + Packet_gameserver resp; + load_packet_gameserver(&resp); + resp.answer(pac); + pac->from->joined=true; + char addr[64]; + Net::stringaddress(addr, pac->from->address(), pac->from->getdestport()); + if(pac && pac->from) + log_step("connection_joined\t%u\t%s", pac->from->id(), pac->registered? "true":"false"); + for(i=0; inet_list.get(i); + if(c) { + Packet_download d; + d.player = i; + for(y=0; y<32; y++) + for(x=0; x<10; x++) { + d.can[y][x] = c->block[y][x+4]; + d.occ[y][x] = c->occupied[y][x+4]; + d.blinded[y][x] = c->blinded[y][x+4]; + } + d.seed = c->rnd.get_seed(); + d.idle = c->idle; + d.state = c->state; + if(c->bloc) { + d.bloc = c->bloc->quel; + d.next = c->next->quel; + d.next2 = c->next2->quel; + d.next3 = c->next3->quel; + } else { + d.bloc = d.next = d.next2 = d.next3 = 255; + } + d.bonus = c->bonus; + int ii; + for(ii=0; ii<20; ii++) { + if(iibonus) { + d.bon[ii].x=c->bon[ii].x; + d.bon[ii].color=c->bon[ii].color; + d.bon[ii].blind_time=c->bon[ii].blind_time; + d.bon[ii].hole_pos=c->bon[ii].hole_pos; + d.bon[ii].final=c->bon[ii].final; + } + else { + d.bon[ii].x=0; + d.bon[ii].color=0; + d.bon[ii].blind_time=0; + d.bon[ii].hole_pos=0; + d.bon[ii].final=false; + } + } + for(ii=0; iiattacks[ii]; + d.last_attacker=c->last_attacker; + net->sendtcp(pac->from, &d); + + Packet_stat p; + p.player=i; + CS::Stat_type last; + if(game->net_version()>=23) + last=CS::LAST; + else + last=CS::LAST_BEFORE_120; + for(j=0; jstats[j].get_value()); + net->sendtcp(pac->from, &p); + } + } + char *motd=game->get_motd(); + if(motd && motd[0]) { + send_msg(pac->from, " "); + send_msg(pac->from, "%s", motd); + } + send_msg(pac->from, " "); + ret(); +} + +void Net_pendingjoin::notify() { + //Check whether the connection got closed and cancel if applicable + int co; + for(co=0; coconnections.size(); co++) + if(net->connections[co]==pac->from) + return; + cancel=true; //Will cancel on next step() +} diff --git a/source/net_stuff.cpp b/source/net_stuff.cpp new file mode 100644 index 0000000..44ea97c --- /dev/null +++ b/source/net_stuff.cpp @@ -0,0 +1,315 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "error.h" +#include "packets.h" +#include "config.h" +#include "game.h" +#include "net_server.h" +#include "canvas.h" +#include "net_stuff.h" +#include "chat_text.h" +#include "texte.h" +#include "video.h" +#include "nglog.h" + +Net_starter::Net_module::Net_module() { + last_video_frame=video->framecount; +} + +void Net_starter::Net_module::step() { + bool loop_only=video_is_dumb; + if(last_video_frame!=video->framecount) { + loop_only=false; + last_video_frame=video->framecount; + } + net->step(loop_only); +} + +char *packet_name[] = { + "P_CHAT", + "P_FINDGAME", + "P_GAMESERVER", + "P_WANTJOIN", + "P_PLAYERWANTJOIN", + "P_CLIENTCHAT", + "P_PLAYERACCEPTED", + "P_PLAYER", + "P_CLIENTPAUSE", + "P_PAUSE", + "P_STAT", + "P_GAMEINFO", + "P_DROPPLAYER", + "P_CLIENTDROPPLAYER", + "P_SERVERDROPPLAYER", + "P_STAMPBLOCK", + "P_CLIENTSTAMPBLOCK", + "P_DEAD", + "P_CLIENTDEAD", + "P_RESPAWN", + "P_CLIENTRESPAWN", + "DEPRECATED_P_WATCH", + "P_STARTWATCH", + "P_CLIENTSTARTWATCH", + "P_DOWNLOAD", + "P_CLIENTLINES", + "P_LINES", + "P_CLIENTTESTPING", + "P_TESTPING", + "P_FIRST_FRAG", + "P_CLIENTFIRST_FRAG", + "P_GONE", + "P_CLIENTGONE", + "P_ENDGAME", + "P_REJOIN", + "P_MOVES", + "P_CLIENTMOVES", + "P_STATE", + "P_CLIENTSTATE", + "P_SERVERSTATE", + "P_SERVERRANDOM", + "P_SERVERPOTATO", + "DEPRECATED_P_SERVERLINES", + "P_SERVERTESTPING", + "P_BYE", + "P_REMOVEBONUS", + "P_CLIENTREMOVEBONUS", + "P_SERVERNAMETEAM", + "P_GAMESTAT", +}; + +int Quadra_param::tcpport() { + return config.info.port_number; +} + +void Quadra_param::print_packet(Packet *p2, char *st) { + if(!st) + return; + if(!p2) { + st[0]=0; + return; + } + if(p2->packet_id==P_STATE) { + Packet_state *p=(Packet_state *) p2; + sprintf(st, "P_STATE: player==%i state==%i", p->player, p->state); + return; + } + if(p2->packet_id==P_CLIENTSTATE) { + Packet_clientstate *p=(Packet_clientstate *) p2; + sprintf(st, "P_CLIENTSTATE: player==%i state==%i", p->player, p->state); + return; + } + if(p2->packet_id==P_SERVERSTATE) { + Packet_serverstate *p=(Packet_serverstate *) p2; + sprintf(st, "P_SERVERSTATE: state==%i", p->state); + return; + } + if(p2->packet_id==P_CHAT) { + Packet_chat *p=(Packet_chat *) p2; + sprintf(st, "P_CHAT: %s", p->text); + return; + } + if(p2->packet_id==P_CLIENTCHAT) { + Packet_clientchat *p=(Packet_clientchat *) p2; + sprintf(st, "P_CLIENTCHAT: %s", p->text); + return; + } + if(p2->packet_id==P_PLAYER) { + Packet_player *p=(Packet_player *) p2; + sprintf(st, "P_PLAYER: %s", p->name); + return; + } + if(p2->packet_id==P_MOVES) { + Packet_moves *p=(Packet_moves *) p2; + sprintf(st, "P_MOVES: %i", p->player); + return; + } + if(p2->packet_id==P_STAMPBLOCK) { + Packet_stampblock *p=(Packet_stampblock *) p2; + sprintf(st, "P_STAMPBLOCK: %i", p->player); + return; + } + sprintf(st, "%s", packet_name[p2->packet_id]); +} + +Packet *Quadra_param::alloc_packet(Word pt) { + switch(pt) { + case P_CHAT: return new Packet_chat(); + case P_FINDGAME: return new Packet_findgame(); + case P_GAMESERVER: return new Packet_gameserver(); + case P_WANTJOIN: return new Packet_wantjoin(); + case P_PLAYERWANTJOIN: return new Packet_playerwantjoin(); + case P_CLIENTCHAT: return new Packet_clientchat(); + case P_PLAYERACCEPTED: return new Packet_playeraccepted(); + case P_PLAYER: return new Packet_player(); + case P_CLIENTPAUSE: return new Packet_clientpause(); + case P_PAUSE: return new Packet_pause(); + case P_STAT: return new Packet_stat(); + case P_GAMEINFO: return new Packet_gameinfo(); + case P_DROPPLAYER: return new Packet_dropplayer(); + case P_CLIENTDROPPLAYER: return new Packet_clientdropplayer(); + case P_SERVERDROPPLAYER: return new Packet_serverdropplayer(); + case P_STAMPBLOCK: return new Packet_stampblock(); + case P_CLIENTSTAMPBLOCK: return new Packet_clientstampblock(); + case P_DEAD: return new Packet_dead(); + case P_CLIENTDEAD: return new Packet_clientdead(); + case P_RESPAWN: return new Packet_respawn(); + case P_CLIENTRESPAWN: return new Packet_clientrespawn(); + case P_STARTWATCH: return new Packet_startwatch(); + case P_CLIENTSTARTWATCH: return new Packet_clientstartwatch(); + case P_DOWNLOAD: return new Packet_download(); + case P_CLIENTLINES: return new Packet_clientlines(); + case P_LINES: return new Packet_lines(); + case P_TESTPING: return new Packet_testping(); + case P_CLIENTTESTPING: return new Packet_clienttestping(); + case P_FIRST_FRAG: return new Packet_first_frag(); + case P_CLIENTFIRST_FRAG: return new Packet_clientfirst_frag(); + case P_GONE: return new Packet_gone(); + case P_CLIENTGONE: return new Packet_clientgone(); + case P_ENDGAME: return new Packet_endgame(); + case P_REJOIN: return new Packet_rejoin(); + case P_MOVES: return new Packet_moves(); + case P_CLIENTMOVES: return new Packet_clientmoves(); + case P_STATE: return new Packet_state(); + case P_CLIENTSTATE: return new Packet_clientstate(); + case P_SERVERSTATE: return new Packet_serverstate(); + case P_SERVERRANDOM: return new Packet_serverrandom(); + case P_SERVERPOTATO: return new Packet_serverpotato(); + case P_SERVERTESTPING: return new Packet_servertestping(); + case P_BYE: return new Packet_bye(); + case P_REMOVEBONUS: return new Packet_removebonus(); + case P_CLIENTREMOVEBONUS: return new Packet_clientremovebonus(); + case P_SERVERNAMETEAM: return new Packet_servernameteam(); + case P_GAMESTAT: return new Packet_gamestat(); + default: return NULL; + } +} + +bool Quadra_param::is_dispatchable(Net_connection *nc, Packet *p) { + if(!p) + return false; + if(!nc) + return false; + if(p->packet_id==P_CHAT) + return true; + if(nc->joined) + return true; + return false; +} + +void Quadra_param::server_deconnect() { + msgbox("Quadra_param::server_deconnect: Game terminated.\n"); + if(game) { + game->abort=true; + if(!game->server) + message(-1, ST_SERVERDECONNECT); + } +} + +bool Quadra_param::accept_connection(Net_connection *nc) { + if(!Net_param::accept_connection(nc)) + return false; + if(!game) + return false; + if(game->server_accept_connection) + return false; + if(!game->network) + return false; + if(!game->net_list.accept_connection(nc)) + return false; + return true; +} + +void Quadra_param::client_connect(Net_connection *adr) { + if(!adr) + return; + char st[64], st1[256]; + Net::stringaddress(st, adr->address(), adr->getdestport()); + log_step("connect\t%u\t%s", adr->id(), st); + sprintf(st1, ST_CONNECTFROMBOB, st); + message(-1, st1, true, false, true); +} + +void Quadra_param::client_deconnect(Net_connection *adr) { + if(!adr) + return; + msgbox("Quadra_param::client_deconnect(%x)\n", adr); + if(game) + game->net_list.client_deconnect(adr); + char st[64], st1[256]; + Net::stringaddress(st, adr->address(), adr->getdestport()); + log_step("disconnect\t%u", adr->id()); + sprintf(st1, ST_DISCONNECTFROMBOB, st); + message(-1, st1, true, false, true); + if(!game) + return; + for(int j=0; jnet_list.get(j); + if(c && c->remote_adr == adr) { + c->remote_adr = NULL; // cette net_connection est desormais detruite. + if(c->idle != 3) { + /* envoie un 'P_GONE' automatique pour tout les joueurs actifs du client qui a ete + deconnecte. + */ + if(c->idle != 2 && !c->dying) { + Packet_dead d; + d.player = j; + d.then_gone = true; + net->dispatch(&d, P_DEAD); + if(game->net_server) + game->net_server->record_packet(&d); + } + else { + Packet_gone p; + p.player = j; + net->dispatch(&p, P_GONE); + if(game->net_server) + game->net_server->record_packet(&p); + } + } + } + } +} + +char *Quadra_param::get_motd() { + if(game && game->get_motd()) + return game->get_motd(); + else + return Net_param::get_motd(); +} + +void send_msg(Net_connection *nc, char *msg, ...) { + if(!nc || !msg) + return; + char st[4096]; + va_list marker; + va_start(marker, msg); + vsprintf(st, msg, marker); + va_end(marker); + if(nc->packet_based) { + Packet_chat p; + p.team=-1; + p.to_team=-1; + strncpy(p.text, st, 255); + p.text[255]=0; + if(game && game->loopback_connection==nc) + chat_text->add_text(p.team, p.text, true); + else + nc->sendtcp(&p); + } + else { + char st2[4120]; + //Output on a separate line if user is typing a line + if(nc->incoming->size()) + sprintf(st2, "\r\n%s\r\n", st); + else + sprintf(st2, "%s\r\n", st); + nc->sendtcp((Byte *) st2, strlen(st2)); + //Re-echo user command to be less annoying + if(nc->incoming->size()) + nc->sendtcp(nc->incoming->get(), nc->incoming->size()); + } +} diff --git a/source/nglog.cpp b/source/nglog.cpp new file mode 100644 index 0000000..5e84996 --- /dev/null +++ b/source/nglog.cpp @@ -0,0 +1,157 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#ifdef UGS_LINUX +#define stricmp strcasecmp +#endif +#ifdef UGS_DIRECTX +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#include "game.h" +#include "overmind.h" +#include "global.h" +#include "nglog.h" + +static Log *local=NULL; + +static char *__nglog_rel_path="NetGamesUSA.com"; +static char *__nglog_ngstats_logdir="logs"; +static char *__nglog_ngstats_cfg="ngStatsQD.cfg"; +void ngLog_ngStatsCall(int server_quit); + +Log::Log(const char *fname) { + strcpy(filename, fname); + file=new Res_dos(filename, RES_CREATE); + exist=file->exist; + if(!exist) { + msgbox("Log::Log: failed to create %s\n", filename); + delete file; + file=NULL; + } +} + +Log::~Log() { + delete file; +} + +void Log::log_event(const char *st) { + if(exist) { + char buf[1024]; + Dword frame=overmind.framecount-game->frame_start; + sprintf(buf, "%u.%02u\t%s\n", frame/100, frame%100, st); + file->write(buf, strlen(buf)); + } +} + +bool log_init(const char *filename) { + char the_name[1024]; + sprintf(the_name, "%s/%s/ngStats/%s/%s", quadradir, __nglog_rel_path, __nglog_ngstats_logdir, filename); + local=new Log(the_name); + return local->exist; +} + +void log_step(const char *st, ...) { + char event[1024]; + va_list marker; + va_start(marker, st); + vsprintf(event, st, marker); + va_end(marker); + if(local) + local->log_event(event); +} + +void log_finalize(char *salt) { + if(local) { + log_step("game_end"); + delete local; + local=NULL; + ngLog_ngStatsCall(false); + } +} + +char *log_team(int t) { + char *team="none"; + switch(t) { + case 0: team="orange"; break; + case 1: team="cyan"; break; + case 2: team="red"; break; + case 3: team="purple"; break; + case 4: team="yellow"; break; + case 5: team="green"; break; + case 6: team="blue"; break; + case 7: team="gray"; break; + } + return team; +} + +char *log_handicap(int h) { + char *handi="unknown"; + switch(h) { + case 0: handi="beginner"; break; + case 1: handi="apprentice"; break; + case 2: handi="intermediate"; break; + case 3: handi="master"; break; + case 4: handi="grand_master"; break; + } + return handi; +} + +// +// void ngLog_ngStatsCall(int server_quit): +// ---------------------------------------- +// +void ngLog_ngStatsCall(int server_quit) { +#ifdef UGS_LINUX + char cmd[2048]; +#endif +#ifdef UGS_DIRECTX + STARTUPINFO si = {0}; + PROCESS_INFORMATION pi = {0}; + char full_cmd[2048]; +#endif + char browse[] = "false"; + char binexe[1024], logpath[1024]; + char ngs_done[1024]; + + + if(server_quit) + strcpy(browse, "true"); + +#ifdef UGS_LINUX + strcpy(ngs_done, quadradir); + sprintf(binexe, "%s/%s/ngStats/bin/ngStatsQuadra", ngs_done, __nglog_rel_path); + sprintf(logpath, "%s/%s/ngStats/%s", ngs_done, __nglog_rel_path, __nglog_ngstats_logdir); + sprintf(cmd, "%s -b %s -c %s %s &", binexe, browse, __nglog_ngstats_cfg, logpath); + // Call to ngStats + system(cmd); +#endif +#ifdef UGS_DIRECTX + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + if(server_quit) + si.wShowWindow = SW_SHOW; + else + si.wShowWindow = SW_HIDE; + si.hStdInput = NULL; + si.hStdOutput = NULL; + si.hStdError = NULL; + + strcpy(ngs_done, quadradir); + sprintf(binexe, "%s\\%s\\ngStats\\ngStatsQD.exe", ngs_done, __nglog_rel_path); + sprintf(logpath, "%s\\%s\\ngStats\\%s", ngs_done, __nglog_rel_path, __nglog_ngstats_logdir); + sprintf(full_cmd, "%s -b %s -c %s\\%s\\ngStats\\%s %s", binexe, browse, ngs_done, __nglog_rel_path, __nglog_ngstats_cfg, logpath); + // Call to ngStats + if(!server_quit) + CreateProcess(NULL, full_cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi); + else + CreateProcess(NULL, full_cmd, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi); + +#endif + + return; +} diff --git a/source/packets.cpp b/source/packets.cpp new file mode 100644 index 0000000..6b33d5d --- /dev/null +++ b/source/packets.cpp @@ -0,0 +1,910 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "net_buf.h" +#include "error.h" +#include "net_stuff.h" +#include "stats.h" +#include "config.h" +#include "canvas.h" +#include "packets.h" + +void Packet_wantjoin::write(Net_buf *p) { + Packet_ping::write(p); + p->write_byte(1); + p->write_byte(net_version); + p->write_byte(language); + p->write_byte(os); + p->write_bool(registered); +} + +bool Packet_wantjoin::read(Net_buf *p) { + if(!Packet_ping::read(p)) + return false; + if(!p->read_byte()) + return false; // completely ignore 1.1.0 clients + net_version=p->read_byte(); + language=p->read_byte(); + os=p->read_byte(); + registered=p->read_bool(); + return true; +} + +bool read_attack(Attack *a, Net_buf *p) { + a->type=(Attack_type) p->read_byte(); + if(a->type>=ATTACK_LAST) + return false; + a->param=p->read_dword(); + return true; +} + +void write_attack(Attack *a, Net_buf *p) { + p->write_byte(a->type); + p->write_dword(a->param); +} + +Packet_gameinfo::Packet_gameinfo() { + packet_id = P_GAMEINFO; + name[0] = 0; + version = 0; + port = 0; + game_end_value = 0; + nolevel_up = 0; + delay_start = false; + terminated = false; + level_start = 0; + combo_min = 0; + allow_handicap = true; + survivor = false; + hot_potato = false; + game_end = 0; +} + +Packet_gameinfo::~Packet_gameinfo() { + players.deleteall(); +} + +void Packet_gameinfo::write(Net_buf *p) { + Packet_udp::write(p); + p->write_string(name); + p->write_byte(version); + p->write_dword(port); + p->write_byte(players.size()); + int i; + for(i=0; iwrite_byte(players[i]->team); + p->write_string(players[i]->name); + } + p->write_dword(game_end_value); + p->write_bool(nolevel_up); + p->write_bool(delay_start); + p->write_bool(terminated); + p->write_byte(level_start); + p->write_byte(combo_min); + p->write_bool(survivor); + p->write_byte(game_end); + for(i=0; iwrite_dword(players[i]->handicap); + } + //allow_handicap is reversed because the default should be true + // but 1.1.1 and older will not supply it (thus we'll read a + // 0 from those). + p->write_bool(!allow_handicap); + p->write_bool(hot_potato); + if(version>=22) { + write_attack(&normal_attack, p); + write_attack(&clean_attack, p); + write_attack(&potato_normal_attack, p); + write_attack(&potato_clean_attack, p); + } +} + +bool Packet_gameinfo::read(Net_buf *p) { + if(!Packet_udp::read(p)) + return false; + if(!p->read_string(name, 32)) + return false; + version = p->read_byte(); + port = p->read_dword(); + Byte num_player = p->read_byte(); + if(num_player>MAXPLAYERS) + return false; + int i; + for(i=0; iread_byte(); + if(t>=MAXTEAMS) + return false; + if(!p->read_string(tmp, 40)) + return false; + add_player(0, t, tmp, -1, 0); + } + game_end_value = p->read_dword(); + nolevel_up = p->read_bool(); + delay_start = p->read_bool(); + terminated = p->read_bool(); + level_start = p->read_byte(); + if(level_start > 10) + return false; + combo_min = p->read_byte(); + if(combo_min > 10) + return false; + survivor = p->read_bool(); + game_end = p->read_byte(); + if(game_end > 4) + return false; + for(i=0; iread_dword(); + if(handicap<0 || handicap>4) + return false; + players[i]->handicap=handicap; + } + //allow_handicap is reversed because the default should be true + // but 1.1.1 and older will not supply it (thus we'll read a + // 0 from those). + allow_handicap=!p->read_bool(); + hot_potato=p->read_bool(); + if(version>=22) { + if(!read_attack(&normal_attack, p)) + return false; + if(!read_attack(&clean_attack, p)) + return false; + if(!read_attack(&potato_normal_attack, p)) + return false; + if(!read_attack(&potato_clean_attack, p)) + return false; + } + return true; +} + +Packet_gameserver::~Packet_gameserver() { + players.deleteall(); +} + +void Packet_gameserver::write(Net_buf *p) { + Packet_ping::write(p); + p->write_byte(version); + p->write_string(name); + p->write_bool(accepted); + p->write_dword(game_seed); + p->write_bool(paused); + p->write_byte(players.size()); + p->write_bool(nolevel_up); + p->write_byte(level_start); + p->write_bool(survivor); + p->write_byte(combo_min); + p->write_word(delay_start); + p->write_byte(game_end); + p->write_dword(game_end_value); + int i; + for(i=0; iwrite_byte(players[i]->quel); + p->write_byte(players[i]->team); + p->write_string(players[i]->name); + } + p->write_bool(wants_moves); + p->write_byte(syncpoint); + p->write_byte(potato_team); + for(i=0; iwrite_dword(players[i]->handicap); + } + //allow_handicap is reversed because the default should be true + // but 1.1.1 and older will not supply it (thus we'll read a + // 0 from those). + p->write_bool(!allow_handicap); + p->write_bool(hot_potato); + if(version>=22) { + write_attack(&normal_attack, p); + write_attack(&clean_attack, p); + write_attack(&potato_normal_attack, p); + write_attack(&potato_clean_attack, p); + } + p->write_bool(single); + p->write_bool(terminated); + for(i=0; iwrite_dword(players[i]->player_id); + } +} + +bool Packet_gameserver::read(Net_buf *p) { + if(!Packet_ping::read(p)) + return false; + version = p->read_byte(); + if(!p->read_string(name, 32)) + return false; + accepted=p->read_bool(); + game_seed=p->read_dword(); + paused=p->read_bool(); + Byte num_player = p->read_byte(); + if(num_player>MAXPLAYERS) + return false; + nolevel_up=p->read_bool(); + level_start=p->read_byte(); + survivor=p->read_bool(); + combo_min=p->read_byte(); + delay_start=p->read_word(); + game_end=p->read_byte(); + if(game_end>=END_LAST) + return false; + game_end_value=p->read_dword(); + int i; + for(i=0; iread_byte(); + if(quel>=MAXPLAYERS) + return false; + Byte t=p->read_byte(); + if(t>=MAXTEAMS) + return false; + if(!p->read_string(tmp, 40)) + return false; + add_player(quel, t, tmp, 0, 0); + } + wants_moves=p->read_bool(); + syncpoint=p->read_byte(); + if(syncpoint>Canvas::LAST) + return false; + potato_team=p->read_byte(); + if(potato_team>=MAXTEAMS && potato_team!=255) + return false; + //Adjust player handicaps (they are at the end to maintain + // net compatibility with net_version <= 20 + for(i=0; iread_dword(); + if(handicap<0 || handicap>4) + return false; + players[i]->handicap=handicap; + } + //allow_handicap is reversed because the default should be true + // but 1.1.1 and older will not supply it (thus we'll read a + // 0 from those). + allow_handicap=!p->read_bool(); + hot_potato=p->read_bool(); + if(version>=22) { + if(!read_attack(&normal_attack, p)) + return false; + if(!read_attack(&clean_attack, p)) + return false; + if(!read_attack(&potato_normal_attack, p)) + return false; + if(!read_attack(&potato_clean_attack, p)) + return false; + } + single=p->read_bool(); + terminated=p->read_bool(); + for(i=0; iplayer_id=p->read_dword(); + } + return true; +} + +bool Packet_gameserver::any_attack() { + if(normal_attack.type!=ATTACK_NONE || clean_attack.type!=ATTACK_NONE) + return true; + if(hot_potato && (potato_normal_attack.type!=ATTACK_NONE || potato_clean_attack.type!=ATTACK_NONE)) + return true; + return false; +} + +bool Packet_chat::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + team = p->read_byte(); + if(team>=MAXTEAMS || team<-1) + return false; + if(!p->read_string(text, 256)) + return false; + to_team = p->read_byte(); + if(to_team>=MAXTEAMS || to_team<-1) + return false; + return true; +} + +void Packet_chat::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_byte(team); + p->write_string(text); + p->write_byte(to_team); +} + +bool Packet_playerwantjoin::read(Net_buf *p) { + if(!Packet_ping::read(p)) + return false; + team = p->read_byte(); + if(team>=MAXTEAMS) + return false; + if(!p->read_string(name, 40)) + return false; + player = p->read_byte(); + if(player>2) + return false; + int repeat = p->read_dword(); + if(repeat<-1 || repeat>3) + return false; + smooth = p->read_dword(); + if(smooth<0 || smooth>1) + return false; + shadow = p->read_dword(); + if(shadow<0 || shadow>1) + return false; + handicap = p->read_dword(); + if(handicap<0 || handicap>4) + return false; + p->read_mem(player_hash, 16); + if(!p->read_string(team_name, 40)) + return false; + p->read_mem(team_hash, 16); + h_repeat = p->read_dword(); + if(h_repeat<0 || h_repeat>3) + return false; + v_repeat = p->read_dword(); + if(v_repeat<0 || v_repeat>3) + return false; + return true; +} + +void Packet_playerwantjoin::write(Net_buf *p) { + Packet_ping::write(p); + p->write_byte(team); + p->write_string(name); + p->write_byte(player); + if(game->net_version()>=23) + p->write_dword(-1); + else + p->write_dword(h_repeat); + p->write_dword(smooth); + p->write_dword(shadow); + p->write_dword(handicap); + p->write_mem(player_hash, 16); + p->write_string(team_name); + p->write_mem(team_hash, 16); + p->write_dword(h_repeat); + p->write_dword(v_repeat); +} + +bool Packet_player::read(Net_buf *p) { + if(!Packet_ping::read(p)) + return false; + team = p->read_byte(); + if(team>=MAXTEAMS) + return false; + if(!p->read_string(name, 40)) + return false; + player = p->read_byte(); + if(player>2) + return false; + int repeat = p->read_dword(); + if(repeat<-1 || repeat>3) + return false; + if(repeat!=-1) + h_repeat = v_repeat = repeat; + smooth = p->read_dword(); + if(smooth<0 || smooth>1) + return false; + shadow = p->read_dword(); + if(shadow<0 || shadow>1) + return false; + pos = p->read_byte(); + if(pos>=MAXPLAYERS) + return false; + handicap = p->read_dword(); + if(handicap<0 || handicap>4) + return false; + int h_repeat = p->read_dword(); + if(h_repeat<0 || h_repeat>3) + return false; + if(repeat==-1 || h_repeat) + this->h_repeat = h_repeat; + int v_repeat = p->read_dword(); + if(v_repeat<0 || v_repeat>3) + return false; + if(repeat==-1 || v_repeat) + this->v_repeat = v_repeat; + player_id = p->read_dword(); + return true; +} + +void Packet_player::write(Net_buf *p) { + Packet_ping::write(p); + p->write_byte(team); + p->write_string(name); + p->write_byte(player); + if(game->net_version()>=23) + p->write_dword(-1); + else + p->write_dword(h_repeat); + p->write_dword(smooth); + p->write_dword(shadow); + p->write_byte(pos); + p->write_dword(handicap); + p->write_dword(h_repeat); + p->write_dword(v_repeat); + p->write_dword(player_id); +} + +bool Packet_playeraccepted::read(Net_buf *p) { + if(!Packet_ping::read(p)) + return false; + pos = p->read_byte(); + if(pos>=MAXPLAYERS) + return false; + accepted = p->read_byte(); + return true; +} + +void Packet_playeraccepted::write(Net_buf *p) { + Packet_ping::write(p); + p->write_byte(pos); + p->write_byte(accepted); +} + +bool Packet_pause::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + player=p->read_byte(); + return true; +} + +void Packet_pause::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_byte(player); +} + +Packet_stat::~Packet_stat() { + net_stats.deleteall(); +} + +void Packet_stat::add_stat(Byte s, int v) { + Net_stat *n = new Net_stat(s, v); + net_stats.add(n); +} + +bool Packet_stat::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + num_stat=p->read_byte(); + for(int i=0; iread_byte(); + if(st>=CS::LAST) + continue; //Ignore stats we don't know about + int val=p->read_dword(); + add_stat(st, val); + } + return true; +} + +void Packet_stat::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_byte(net_stats.size()); + for(int i=0; iwrite_byte(net_stats[i]->st); + p->write_dword(net_stats[i]->value); + } +} + +Packet_gamestat::~Packet_gamestat() { + net_stats.deleteall(); +} + +void Packet_gamestat::add_stat(Byte s, int v) { + Net_stat *n = new Net_stat(s, v); + net_stats.add(n); +} + +bool Packet_gamestat::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + num_stat=p->read_byte(); + for(int i=0; iread_byte(); + if(st>=GS::LAST) + continue; //Ignore stats we don't know about + int val=p->read_dword(); + add_stat(st, val); + } + return true; +} + +void Packet_gamestat::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_byte(net_stats.size()); + for(int i=0; iwrite_byte(net_stats[i]->st); + p->write_dword(net_stats[i]->value); + } +} + +bool Packet_dropplayer::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + Byte r=p->read_byte(); + if(r>=DROP_LAST) + return false; + reason=(Drop_reason) r; + return true; +} + +void Packet_dropplayer::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_byte(reason); +} + +bool Packet_playerbase::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + player=p->read_byte(); + if(player>=MAXPLAYERS) + return false; + return true; +} + +void Packet_playerbase::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_byte(player); +} + +bool Packet_stampblock::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + x=p->read_byte(); + y=p->read_byte(); + rotate=p->read_byte(); + score=p->read_byte(); + date=p->read_word(); + block_rotated=p->read_byte(); + time_held=p->read_word(); + return true; +} + +void Packet_stampblock::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_byte(x); + p->write_byte(y); + p->write_byte(rotate); + p->write_byte(score); + p->write_word(date); + p->write_byte(block_rotated); + p->write_word(time_held); +} + +bool Packet_dead::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + then_gone=p->read_bool(); + return true; +} + +void Packet_dead::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_bool(then_gone); +} + +bool Packet_startwatch::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + address=p->read_dword(); + stop=p->read_bool(); + update=p->read_byte(); + return true; +} + +void Packet_startwatch::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_dword(address); + p->write_bool(stop); + p->write_byte(update); +} + +bool Packet_download::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + p->read_mem(can, sizeof(can)); + int i, j; + for(j=0; j<32; j++) + for(i=0; i<10; i++) { + occ[j][i]=can[j][i]? true:false; + can[j][i] &= 0x7F; + } + seed = p->read_dword(); + idle = p->read_byte(); + if(idle>3) + return false; + state = p->read_byte(); + if(state>Canvas::LAST) + return false; + bloc = p->read_byte(); + if(bloc>6 && bloc!=255) + return false; + next = p->read_byte(); + if(next>6 && bloc!=255) + return false; + next2 = p->read_byte(); + if(next2>6 && bloc!=255) + return false; + next3 = p->read_byte(); + if(next3>6 && bloc!=255) + return false; + bonus = p->read_byte(); + if(bonus>20) + return false; + for(i=0; i<20; i++) { + if(iread_byte(); + bon[i].color=p->read_byte(); + bon[i].blind_time=0; //Will be fixed below + bon[i].hole_pos=0; + bon[i].final=false; + } + else { + p->read_byte(); + p->read_byte(); + bon[i].x=0; + bon[i].color=0; + bon[i].blind_time=0; + bon[i].hole_pos=0; + bon[i].final=false; + } + } + for(i=0; iread_byte(); + last_attacker = p->read_byte(); + if(last_attacker>=MAXPLAYERS && last_attacker!=255) + return false; + p->read_mem(blinded, sizeof(blinded)); + for(i=0; i<20; i++) + if(iread_byte(); + else + p->read_byte(); + for(i=0; iread_word(); + bon[i].hole_pos=tmp & 0x3FF; + bon[i].final=tmp&0x8000? true:false; + } + return true; +} + +void Packet_download::write(Net_buf *p) { + Packet_playerbase::write(p); + Byte tmp[32][10]; + memcpy(tmp, can, sizeof(can)); + int i, j; + if(game->net_version()>=23) + for(j=0; j<32; j++) + for(i=0; i<10; i++) + if(occ[j][i]) + tmp[j][i] |= 0x80; + p->write_mem(tmp, sizeof(can)); + p->write_dword(seed); + p->write_byte(idle); + p->write_byte(state); + p->write_byte(bloc); + p->write_byte(next); + p->write_byte(next2); + p->write_byte(next3); + p->write_byte(bonus); + for(i=0; i<20; i++) + if(iwrite_byte(bon[i].x); + p->write_byte(bon[i].color); + } + else { + p->write_byte(0); + p->write_byte(0); + } + for(i=0; iwrite_byte(attacks[i]); + p->write_byte(last_attacker); + p->write_mem(blinded, sizeof(blinded)); + for(i=0; i<20; i++) + if(iwrite_byte(bon[i].blind_time); + else + p->write_byte(0); + for(i=0; iwrite_word(tmp); + } +} + +bool Packet_lines::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + nb=p->read_byte(); + if(nb>36) + return false; + nc=p->read_byte(); + lx=p->read_byte(); + //127 is magical code for new complexity stuff + if((lx<4 || lx>=14) && lx!=127) + return false; + sender=p->read_byte(); + if(sender>=MAXPLAYERS && sender!=255) + return false; + attack.type=(Attack_type) p->read_byte(); + if(attack.type>=ATTACK_LAST) + return false; + attack.param=p->read_dword(); + for(int i=0; iread_word(); + return true; +} + +void Packet_lines::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_byte(nb); + p->write_byte(nc); + p->write_byte(lx); + p->write_byte(sender); + p->write_byte(attack.type); + p->write_dword(attack.param); + for(int i=0; iwrite_word(hole_pos[i]); +} + +bool Packet_testping::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + frame=p->read_dword(); + return true; +} + +void Packet_testping::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_dword(frame); +} + +bool Packet_gone::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + chat_msg=!p->read_bool(); + return true; +} + +void Packet_gone::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_bool(!chat_msg); +} + +bool Packet_endgame::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + auto_end=p->read_bool(); + return true; +} + +void Packet_endgame::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_bool(auto_end); +} + +bool Packet_rejoin::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + int repeat = p->read_dword(); + if(repeat<-1 || repeat>3) + return false; + smooth = p->read_dword(); + if(smooth<0 || smooth>1) + return false; + shadow = p->read_dword(); + if(shadow<0 || shadow>1) + return false; + handicap = p->read_dword(); + if(handicap<0 || handicap>4) + return false; + h_repeat = p->read_dword(); + if(h_repeat<0 || h_repeat>3) + return false; + v_repeat = p->read_dword(); + if(v_repeat<0 || v_repeat>3) + return false; + return true; +} + +void Packet_rejoin::write(Net_buf *p) { + Packet_playerbase::write(p); + if(game->net_version()>=23) + p->write_dword(-1); + else + p->write_dword(h_repeat); + p->write_dword(smooth); + p->write_dword(shadow); + p->write_dword(handicap); + p->write_dword(h_repeat); + p->write_dword(v_repeat); +} + + +void Packet_moves::start_byte() { + if(size==255) + (void)new Error("Packet_moves too big!"); + moves[size]=0; +} + +void Packet_moves::set_bit(int v) { + moves[size] |= v; +} + +void Packet_moves::write_byte() { + size++; +} + +bool Packet_moves::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + size=p->read_byte(); + p->read_mem(moves, size); + return true; +} + +void Packet_moves::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_byte(size); + p->write_mem(moves, size); +} + +bool Packet_state::read(Net_buf *p) { + if(!Packet_playerbase::read(p)) + return false; + state=p->read_byte(); + if(state>Canvas::LAST) + return false; + return true; +} + +void Packet_state::write(Net_buf *p) { + Packet_playerbase::write(p); + p->write_byte(state); +} + +void Packet_serverrandom::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_dword(seed); +} + +bool Packet_serverrandom::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + seed=p->read_dword(); + return true; +} + +bool Packet_serverpotato::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + team=p->read_byte(); + if(team>=MAXTEAMS && team!=255) + return false; + potato_lines=p->read_dword(); + return true; +} + +void Packet_serverpotato::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_byte(team); + p->write_dword(potato_lines); +} + +void Packet_servernameteam::write(Net_buf *p) { + Packet_tcp::write(p); + p->write_byte(team); + p->write_string(name); +} + +bool Packet_servernameteam::read(Net_buf *p) { + if(!Packet_tcp::read(p)) + return false; + team=p->read_byte(); + if(team>=MAXTEAMS) + return false; + if(!p->read_string(name, 40)) + return false; + return true; +} diff --git a/source/pane.cpp b/source/pane.cpp new file mode 100644 index 0000000..e4f7de4 --- /dev/null +++ b/source/pane.cpp @@ -0,0 +1,2064 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "input.h" +#include "zone_text_clock.h" +#include "quadra.h" +#include "config.h" +#include "multi_player.h" +#include "music.h" +#include "chat_text.h" +#include "bloc.h" +#include "game.h" +#include "net_server.h" +#include "packets.h" +#include "texte.h" +#include "global.h" +#include "sons.h" +#include "recording.h" +#include "canvas.h" +#include "crypt.h" +#include "unicode.h" +#include "pane.h" + +Pane_info::Pane_info(Bitmap *bit, Font *f2, Inter *in, int j, Multi_player *pmp) { + fond = bit; + font2 = f2; + inter = in; + x = j*214; + y = 37; + w = 212; + h = 480-y; + mp = pmp; + quel_pane = j; + back = new Bitmap((*bit)[y]+x, w, 18*20, bit->realwidth); + back_bottom = new Bitmap((*bit)[y+18*20]+x, w, 480-y-18*20, bit->realwidth); +} + +Pane_info::~Pane_info() { + delete back_bottom; + delete back; +} + +Pane::Pane(const Pane_info &p, bool dback, bool dbottom): + Zone(p.inter, p.x, p.y, p.w, p.h), + pi(p) { + hiden = false; + screen = Video_bitmap::New(pi.x, pi.y, pi.w, pi.h); + draw_background = dback; + draw_bottom = dbottom; +} + +Pane::~Pane() { + delete screen; + video->need_paint = 2; +} + +void Pane::hidecall(Module *m) { + hide(); + call(m); +} + +void Pane::hideexec(Module *m) { + hide(); + exec(m); +} + +void Pane::set_net_pane(int i) { + if(!playback && !game->single) + config.info.pane[pi.quel_pane] = i; +} + +void Pane::ifdone() { + if(done) + set_net_pane(0); +} + +void Pane::draw() { + Zone::draw(); + screen->setmem(); + if(draw_background) + pi.back->draw(screen,0,0); + if(draw_bottom) + pi.back_bottom->draw(screen,0,18*20); +} + +void Pane::hide() { + if(hiden == true) + return; + hide_item(); + hiden = true; +} + +void Pane::hide_item() { + for(int i=0; idisable(); + disable(); +} + +void Pane::show() { + if(hiden == false) + return; + show_item(); + hiden = false; +} + +void Pane::show_item() { + for(int i=0; ienable(); + enable(); +} + +void Pane::step() { + clicked = inter->clicked; + show(); +} + +Pane_option::Pane_option(const Pane_info &p): Pane(p) { + int x2=x+15; + int y2=69, y_height=22; + int i; + if(!playback) { + for(i=0; i<3; i++) { + sprintf(st, ST_STARTBOB, config.player[i].name); + player[i] = new Zone_text_button2(inter, pi.fond, pi.font2, st, x2, y2, w-44); + y2+=y_height; + zone.add(player[i]); + } + } + y2+=y_height; + player_info = new Zone_text_button2(inter, pi.fond, pi.font2, ST_SHOWPLAYER, x2, y2, w-44); y2+=y_height; + zone.add(player_info); + block_info = new Zone_text_button2(inter, pi.fond, pi.font2, ST_BLOCKINFO, x2, y2, w-44); y2+=y_height; + zone.add(block_info); + combo_info = new Zone_text_button2(inter, pi.fond, pi.font2, ST_LINESINFO, x2, y2, w-44); y2+=y_height; + zone.add(combo_info); + select_scheme = new Zone_text_button2(inter, pi.fond, pi.font2, ST_SELECTSCHEME, x2, y2, w-44); y2+=y_height; + zone.add(select_scheme); + + chat_window = server = quit = NULL; + + chat_window = new Zone_text_button2(inter, pi.fond, pi.font2, ST_CHATWINDOW, x2, y2, w-44); y2+=y_height; + zone.add(chat_window); + y2+=y_height; + if(game->network) { + if(!playback) { + server = new Zone_text_button2(inter, pi.fond, pi.font2, ST_SERVERTOOL, x2, y2, w-44); y2+=y_height; + zone.add(server); + } + } + if(pi.quel_pane == 2 && !game->single && !playback) { + quit = new Zone_text_button2(inter, pi.fond, pi.font2, ST_QUIT, 560, 450); + zone.add(quit); + } + game->net_list.add_watch(this); +} + +Pane_option::~Pane_option() { + game->net_list.remove_watch(this); +} + +void Pane_option::init() { + Pane::init(); + Byte t = config.info.pane[pi.quel_pane]; + if(!game->network) { + if(t==6) // pas de Pane_server en mode local + t = 0; + } + if(playback && !playback->single()) { + switch(pi.quel_pane) { + case 0: t=2; break; + case 1: t=2; break; + case 2: t=3; break; + } + } + switch(t) { + case 1: hidecall(new Pane_selectscheme(pi)); break; + case 2: hidecall(new Pane_playerinfo(pi)); break; + case 3: hidecall(new Pane_chat(pi)); break; + case 4: hidecall(new Pane_blockinfo(pi)); break; + case 5: hidecall(new Pane_comboinfo(pi)); break; + case 6: hidecall(new Pane_server(pi)); break; + } +} + +void Pane_option::step() { + Pane::step(); + if(!Pane::clicked) + return; + if(Pane::clicked == select_scheme) { + hidecall(new Pane_selectscheme(pi)); + } + if(Pane::clicked == player_info) { + hidecall(new Pane_playerinfo(pi)); + } + if(Pane::clicked == chat_window) { + hidecall(new Pane_chat(pi)); + } + if(Pane::clicked == block_info) { + hidecall(new Pane_blockinfo(pi)); + } + if(Pane::clicked == combo_info) { + hidecall(new Pane_comboinfo(pi)); + } + if(Pane::clicked == server) { + hidecall(new Pane_server(pi)); + } + if(Pane::clicked == quit) { + game->abort = true; + } + for(int i=0; i<3; i++) { + if(Pane::clicked == player[i]) { + hidecall(new Pane_pre_start(pi, i)); + break; + } + } +} + +void Pane_option::notify() { + if(playback) + return; + int i, j; + for(j=0; j<3; j++) { + bool found = false; + for(i=0; inet_list.get(i); + if(c && c->islocal() && c->player == j) { + found = true; + break; + } + } + if(found) + sprintf(st, ST_RESUMEBOB, config.player[j].name); + else + sprintf(st, ST_STARTBOB, config.player[j].name); + + player[j]->set_text(st); + } +} + +Pane_pre_start::Pane_pre_start(const Pane_info &p, int q): Pane(p) { + qplayer = q; + for(int i=0; i<3; i++) + if(pi.mp->pane[i]) { + Pane_option *po = (Pane_option *) pi.mp->pane[i]; + po->player[qplayer]->disable(); + } +} + +Pane_pre_start::~Pane_pre_start() { + for(int i=0; i<3; i++) + if(pi.mp->pane[i]) { + Pane_option *po = (Pane_option *) pi.mp->pane[i]; + po->player[qplayer]->enable(); + } +} + +void Pane_pre_start::init() { + Pane::init(); + Canvas *canvas = NULL; + for(int j=0; jnet_list.get(j); + if(c && c->islocal() && c->player == qplayer) { + canvas = c; + } + } + if(canvas == NULL) + exec(new Pane_playerstartup(pi, qplayer)); + else + exec(new Pane_startgame(pi, qplayer, canvas)); +} + +Pane_singleplayer::Pane_singleplayer(const Pane_info &p): Pane(p) { + if(!playback) { + int x2=x+15; + for(int i=0; i<3; i++) { + sprintf(st, ST_STARTBOB,config.player[i].name); + player[i] = new Zone_text_button2(inter, pi.fond, pi.font2, st, x2, i*22+ 69); + zone.add(player[i]); + } + } +} + +void Pane_singleplayer::step() { + Pane::step(); + int i; + if(!playback) { + for(i=0; i<3; i++) { + if(Pane::clicked == player[i]) { + hideexec(new Pane_playerjoin(pi, i)); + } + } + } + else { + if(game) + for(i=0; inet_list.get(i); + if(c) + hideexec(new Pane_startgame(pi, -1, c)); + } + } +} + +bool Pane_close::global_clock_visible = false; + +Pane_close::Pane_close(const Pane_info &p, bool dback, bool dbottom): + Pane(p, dback, dbottom) { + close = NULL; + if(!game->single) + if(!(playback && playback->auto_demo)) + close = new Zone_text_button2(pi.inter, pi.fond, pi.font2, ST_CLOSE, pi.x+146, 10); + clock = NULL; + clock_visible = false; + seconds = 0; +} + +Pane_close::~Pane_close() { + if(close) + delete close; + if(clock) { + delete clock; + if(clock_visible) + global_clock_visible = false; + } +} + +void Pane_close::hide_item() { + Pane::hide_item(); + if(close) + close->disable(); + if(clock && clock_visible) { + clock->disable(); + clock_visible = global_clock_visible = false; + } +} + +void Pane_close::show_item() { + Pane::show_item(); + if(close) + close->enable(); + if(clock) + show_clock(); +} + +void Pane_close::show_clock() { + if(!clock_visible && !global_clock_visible) { + clock->enable(); + clock_visible = global_clock_visible = true; + } +} + +void Pane_close::step() { + Pane::step(); + if(clicked) { + if(clicked == close) { + ret(); + } + } + if(clock) { + show_clock(); + if(clock_visible) + update_clock(); + } +} + +void Pane_close::update_clock() { + Dword timer=game->net_list.gettimer(); + if(timer < (Dword)game->game_end_value) + seconds=(game->game_end_value - timer+99)/100; + else + seconds=0; +} + +void Pane_close::allow_clock() { + if(game->game_end == 2) { + update_clock(); + clock = new Zone_text_clock(inter, &seconds, x+10, 10, 90, false, pi.mp->courrier); + clock->disable(); + show_clock(); + } +} + +Pane_selectscheme::Pane_selectscheme(const Pane_info &p): + Pane_close(p) { + int i; + for(i=0; i<3; i++) + ((Pane_option *) pi.mp->pane[i])->select_scheme->disable(); + + bit = pi.mp->bit; + pal = &pi.mp->pal; + zone.add(new Zone_text(pi.inter, ST_SELECTCOLORSCHEME, pi.x+19, 50)); + for(i=0; i<=config.info.unlock_theme; i++) { + sprintf(st, ST_FROMLEVEL,i+1); + level[i] = new Zone_text_button2(pi.inter, pi.fond, pi.font2, st, pi.x+39, 70+i*22); + zone.add(level[i]); + } + if(_debug) { + debugscheme = new Zone_text_button2(pi.inter, pi.fond, pi.font2, "Black", pi.x+39, 70+i*22); + zone.add(debugscheme); + } + else + debugscheme = NULL; + set_net_pane(1); + allow_clock(); +} + +Pane_selectscheme::~Pane_selectscheme() { + for(int i=0; i<3; i++) + if(pi.mp->pane[i]) + ((Pane_option *) pi.mp->pane[i])->select_scheme->enable(); +} + +void Pane_selectscheme::step() { + Pane_close::step(); + ifdone(); + int i; + for(i=0; i<=config.info.unlock_theme; i++) { + if(clicked && clicked == level[i]) { + Canvas::change_level(i+1, pal, bit); + pi.inter->font->colorize(*pal, 255, 255, 255); + pi.mp->courrier->colorize(*pal, 255, 255, 255); + if(config.info.cdmusic == 1) + music->play(i+2, true); + video->setpal(*pal); + if(!playback) { + config.info.multi_level = i+1; + config.write(); + } + } + } + if(clicked && clicked == debugscheme) { + Canvas::change_level(-1, pal, bit); + } +} + +Pane_server::Zone_update_rate::Zone_update_rate(Inter* in, const Palette &pal, int px, int py, int pw): + Zone_text_input(in, pal, (sprintf(port_st, "%i",config.info.update_rate), port_st), 3, px, py, pw) { +} + +void Pane_server::Zone_update_rate::lost_focus(int cancel) { + if(!cancel) { + int port_num; + if(sscanf(Zone_text_input::st, "%i", &port_num) != 1) + port_num = 0; + if(port_num >= 0 && port_num <=99 && !playback) { + config.info.update_rate = port_num; + config.write(); + } else { // si invalide, cancel l'entree + cancel = 1; + } + } + Zone_text_input::lost_focus(cancel); +} + +Pane_server::Pane_server(const Pane_info &p): + Pane_close(p) { + int i; + for(i=0; i<3; i++) + ((Pane_option *) pi.mp->pane[i])->server->disable(); + + zone.add(new Zone_text(pi.inter, ST_SERVERTOOL, pi.x+19, 50)); + + int x2=x+15; + int y2=80, y_height=40; + Zone *temp; + + if(game->server) { + drop_player = new Zone_text_button2(inter, pi.fond, pi.font2, ST_DROPPLAYER, x2, y2, w-44); y2+=y_height; + zone.add(drop_player); + drop_connection = new Zone_text_button2(inter, pi.fond, pi.font2, ST_DROPCONNECTION, x2, y2, w-44); y2+=y_height; + zone.add(drop_connection); + + { + Zone_state_text2 *temp = new Zone_state_text2(inter, &game->server_accept_player, x2 + 120, y2+17); + temp->add_string(ST_YES); + temp->add_string(ST_NO); + accept_player = temp; + } + zone.add(accept_player); + temp = new Zone_text(pi.inter, ST_ACCEPTPLAYER, x2, y2); + accept_player->set_child(temp); + zone.add(temp); + y2+=y_height; + + { + Zone_state_text2 *temp = new Zone_state_text2(inter, &game->server_accept_connection, x2 + 120, y2+17); + temp->add_string(ST_YES); + temp->add_string(ST_NO); + accept_connection = temp; + } + zone.add(accept_connection); + temp = new Zone_text(pi.inter, ST_ACCEPTCONNECTION, x2, y2); + accept_connection->set_child(temp); + zone.add(temp); + y2+=y_height+5; + } else { + drop_player = drop_connection = accept_player = accept_connection = NULL; + } + test_ping = new Zone_text_button2(inter, pi.fond, pi.font2, ST_TESTPING, x2, y2, w-44); y2+=y_height; + zone.add(test_ping); + + zone.add(new Zone_text(pi.inter, ST_SETUPDATESPEED, x2, y2)); + zone.add(new Zone_text(pi.inter, ST_SETUPDATESPEED2, x2, y2+20)); + zone.add(new Zone_update_rate(inter, pi.mp->pal, x2 + 20, y2+37, 60)); + y2 += 37+y_height; + + check_ip = new Zone_text_button2(inter, pi.fond, pi.font2, ST_IPINFO, x2, y2, w-44); y2+=y_height; + zone.add(check_ip); + + set_net_pane(6); + allow_clock(); +} + +Pane_server::~Pane_server() { + for(int i=0; i<3; i++) + if(pi.mp->pane[i]) + ((Pane_option *) pi.mp->pane[i])->server->enable(); +} + +void Pane_server::step() { + Pane_close::step(); + ifdone(); + if(clicked) { + if(clicked == drop_player) { + hidecall(new Pane_server_drop_player(pi)); + } + if(clicked == drop_connection) { + hidecall(new Pane_server_drop_connection(pi)); + } + if(clicked == test_ping) { + hidecall(new Pane_server_ping(pi)); + } + if(clicked == check_ip) { + hidecall(new Pane_server_ip(pi)); + } + } +} + +Pane_server_drop_player::Pane_server_drop_player(const Pane_info &p): + Pane_close(p) { + + b_drop = new Zone_text_button2(inter, pi.fond, pi.font2, ST_DROPTHISPLAYER, x+15, 340, w-44); + zone.add(b_drop); + zone.add(new Zone_text(inter, ST_DROPPLAYER, x+9, 50, w-30)); + list_player = new Zone_listbox2(inter, pi.fond, inter->font, &selected_player, x+19, 80, 160, 220); + zone.add(list_player); + game->net_list.add_watch(this); + notify(); + allow_clock(); +} + +Pane_server_drop_player::~Pane_server_drop_player() { + game->net_list.remove_watch(this); +} + +void Pane_server_drop_player::notify() { + list_player->clear(); + for(int i=0; inet_list.get(i); + if(c) { + List_player *l = new List_player(c->name, i, fteam[c->color]); + list_player->add_item(l); + } + } + list_player->dirt(); +} + +void Pane_server_drop_player::step() { + Pane_close::step(); + bool drop = false; + if(clicked == b_drop) + drop = true; + if(inter->double_clicked && list_player->in_listbox(inter->double_clicked)) + drop = true; + if(drop && selected_player != -1) { + List_player *lp = (List_player *) list_player->get_selected(); + game->net_list.server_drop_player(lp->player, DROP_MANUAL); + } +} + +Pane_server_drop_connection::Pane_server_drop_connection(const Pane_info &p): + Pane_close(p) { + b_drop = new Zone_text_button2(inter, pi.fond, pi.font2, ST_DROPTHISCONNECTION, x+15, 340, w-44); + zone.add(b_drop); + zone.add(new Zone_text(inter, ST_DROPCONNECTION, x+9, 50, w-30)); + list_connection = new Zone_listbox2(inter, pi.fond, pi.font2, &selected, x+19, 80, 160, 220); + zone.add(list_connection); + game->net_list.add_watch(this); + net->add_watch(this); + notify(); + allow_clock(); +} + +Pane_server_drop_connection::~Pane_server_drop_connection() { + net->remove_watch(this); + game->net_list.remove_watch(this); +} + +void Pane_server_drop_connection::notify() { + list_connection->clear(); + for(int i=0; iconnections.size(); i++) { + Net_connection *nc=net->connections[i]; + if(nc == game->loopback_connection) + continue; // skip l'adresse local + char st2[256]; + Net::stringaddress(st2, nc->address()); + sprintf(st, "%s:%i", st2, nc->getdestport()); + List_connection *l = new List_connection(st, nc); + list_connection->add_item(l); + for(int j=0; jnet_list.get(j); + if(c && c->remote_adr == nc) { + sprintf(st, "[%s]", c->name); + List_connection *l2 = new List_connection(st, nc, fteam[c->color]); + list_connection->add_item(l2); + } + } + } + list_connection->dirt(); +} + +void Pane_server_drop_connection::step() { + Pane_close::step(); + bool drop = false; + if(clicked == b_drop) + drop = true; + if(inter->double_clicked && list_connection->in_listbox(inter->double_clicked)) + drop = true; + if(drop && selected != -1) { + List_connection *l = (List_connection *) (list_connection->get_selected()); + msgbox("Pane_server_drop_connection: dropping address %x (%s)...\n", l->c->address(), l->list_name); + l->c->disconnect(); + notify(); // force un update de la fenetre (pour enlever l'item detruit) + } +} + +Pane_server_ping::Pane_server_ping(const Pane_info &p): Pane_close(p) { + zone.add(new Zone_text(inter, ST_TESTPING, x+9, 50, w-30)); + zone.add(new Zone_text(inter, ST_PINGDELAY, x+9, 100, w-30)); + pingtime = moyenne = total = nombre = 0; + zone.add(new Zone_text_field(inter, &pingtime, x+19, 130, 90)); + zone.add(new Zone_text(inter, ST_PINGMOYEN, x+9, 190, w-30)); + zone.add(new Zone_text_field(inter, &moyenne, x+19, 220, 90)); + net->addwatch(P_TESTPING, this); + test_delay = 60; + allow_clock(); +} + +Pane_server_ping::~Pane_server_ping() { + net->removewatch(P_TESTPING, this); +} + +void Pane_server_ping::step() { + Pane_close::step(); + if(test_delay == 1) { + send_test(); + } + if(test_delay > 1) + test_delay--; + +} + +void Pane_server_ping::send_test() { + Packet_clienttestping p; + last_frame = p.frame = overmind.framecount; + net->sendtcp(&p); + test_delay = 0; +} + +void Pane_server_ping::net_call(Packet *p2) { + Packet_testping *p = (Packet_testping *) p2; + Dword delay = overmind.framecount - p->frame; + pingtime = delay*10; + nombre++; + total += delay; + moyenne = (total*10)/nombre; + test_delay = 200; +} + +Pane_server_ip::Pane_server_ip(const Pane_info &p): Pane_close(p) { + zone.add(new Zone_text(inter, ST_IPINFO, x+9, 50, w-30)); + zone.add(new Zone_text(inter, ST_HOSTNAME, x+9, 100)); + zone.add(new Zone_text_field(inter, net->host_name, x+29, 120, 140)); + zone.add(new Zone_text(inter, ST_HOSTLIST, x+9, 140)); + Zone_listbox *list; + list = new Zone_listbox2(inter, pi.fond, pi.font2, NULL, x+29, 160, 140, 200); + for(int i=0; ihost_adr.size(); i++) { + Net::stringaddress(st, net->host_adr[i]); + list->add_item(st); + } + zone.add(list); + allow_clock(); +} + +Pane_playerinfo::Pane_playerinfo(const Pane_info &p): Pane_close(p, true, true) { + auto_watch=false; + game->net_list.add_watch(this); + show_quoi = 0; + o_show_val = 0; + show_button = auto_button = NULL; + clear_tag(); + refresh(); + set_net_pane(2); + allow_clock(); + if(playback && playback->auto_demo) + activate_auto_watch(); + else + deactivate_auto_watch(); +} + +Pane_playerinfo::~Pane_playerinfo() { + game->net_list.remove_watch(this); +} + +void Pane_playerinfo::activate_auto_watch() { + auto_watch=true; + if(auto_button) + auto_button->set_font(fteam[5]); +} + +void Pane_playerinfo::deactivate_auto_watch() { + auto_watch=false; + if(auto_button) + auto_button->set_font(inter->font); +} + +void Pane_playerinfo::notify() { + refresh(); +} + +void Pane_playerinfo::process() { + Pane_close::process(); + int i,j; + for(i=0; inet_list.get(i); + if(c) { + if(show_quoi == 0) + total1[c->color] += c->stats[CS::DEATH].get_value(); + total0[c->color] += c->stats[quel_stat()].get_value(); + } else { + for(j=0; j<4; j++) + if(tagged[j] == i) { + tagged[j] = -1; + break; + } + } + } +} + +void Pane_playerinfo::refresh() { + deleteall(); + o_show_val=0; + int x2=x+11; + int y2=42, y_height=20; + int i; + + for(i=0; iadd_string(ST_SHOWFRAG); + temp->add_string(ST_SHOWSCORE); + temp->add_string(ST_SHOWLINE); + temp->add_string(ST_SHOWBLOC); + temp->add_string(ST_SHOWBPM); + temp->add_string(ST_SHOWPPM); + zone.add(temp); + o_show_val=show_quoi; + bool count=false; + + for(int team=0; teamnet_list.get(i); + if(c && c->color == team) { + if(!c->islocal()) + count = true; + if(nb == 0) + y2 += 6; + add_name(c, i, x2, y2); + y2+=y_height; + nb++; + } + } + if(nb > 1) { + add_total(team, x2, y2); + y2+=y_height; + } + } + + for(i=0; i<4; i++) // epure ceux 'tagged' qui sont maintenant NULL (car dropper) + if(tagged[i] != -1) { + Canvas *c = game->net_list.get(tagged[i]); + if(!c) + tagged[i] =-1; + } + + show_button = auto_button = NULL; + if(game->network && !(playback && playback->auto_demo)) { + y2 += 6; + if(count) { + show_button = new Zone_text_button2(inter, pi.fond, pi.font2, ST_SHOWSELECTED, x2+12, y2); + zone.add(show_button); + y2 += 24; + } + auto_button = new Zone_text_button2(inter, pi.fond, pi.font2, ST_AUTOWATCH, x2+24, y2); + zone.add(auto_button); + if(auto_watch) + activate_auto_watch(); // pour que le bouton soit vert au debut + } + + if(hiden) { + for(i=0; ienabled == 0) + zone[i]->disable(); + } else { + dirt(); + } + +} + +void Pane_playerinfo::tag(int q) { + int i; + player[q]->dirt(); + + for(i=0; i<4; i++) + if(tagged[i] == q) { + tagged[i] = -1; + player[q]->set_font(fteam[game->net_list.get(q)->color]); + return; + } + + for(i=0; i<4; i++) + if(tagged[i] == -1) { + break; + } + if(i == 4) { // si aucune place de libre: flush le 4e selected + tag(tagged[3]); + for(int j=3; j>0; j--) + tagged[j] = tagged[j-1]; // rotate les tagged + i = 0; + } + tagged[i] = q; + player[q]->set_font(inter->font); +} + +void Pane_playerinfo::clear_tag() { + for(int i=0; i<4; i++) + tagged[i] = -1; +} + +void Pane_playerinfo::add_name(Canvas *c, int i, int x2, int y2) { + Zone_text *play; + Font *f = fteam[c->color]; + if(c->islocal()) + play = new Zone_text(inter, c->long_name(true, false), x2, y2); + else { + play = new Zone_text_button2(inter, pi.fond, f, c->long_name(), x2, y2, 102); + for(int j=0; j<4; j++) + if(tagged[j] == i) { + f = inter->font; + break; + } + } + play->set_font(f); + zone.add(play); + Zone *show; + int width = 70; + if(show_quoi == 0) { + width = 33; + Zone *death = new Zone_text_field(inter, c->stats[CS::DEATH].get_address(), x+w-58, y2, width, pi.mp->courrier); + zone.add(death); + total1[c->color] += c->stats[CS::DEATH].get_value(); + } + show = new Zone_text_field(inter, c->stats[quel_stat()].get_address(), x+w-95, y2, width, pi.mp->courrier); + zone.add(show); + total0[c->color] += c->stats[quel_stat()].get_value(); + + player[i] = play; +} + +void Pane_playerinfo::add_total(int team, int x2, int y2) { + Zone_text *play; + play = new Zone_text(inter, ST_TOTAL, x2+20, y2); + play->set_font(fteam[team]); + zone.add(play); + Zone *show; + int width = 90, x_start = x+w-25-width; + if(show_quoi == 0) { + width = 33; + Zone *death = new Zone_text_field(inter, &total1[team], x+w-58, y2, width, pi.mp->courrier); + zone.add(death); + x_start = x+w-52-width-10; + } + + show = new Zone_text_field(inter, &total0[team], x_start, y2, width, pi.mp->courrier); + zone.add(show); +} + +int Pane_playerinfo::quel_stat() const { + switch(show_quoi) { + case 1: return CS::SCORE; + case 2: return CS::LINESTOT; + case 3: return CS::COMPTETOT; + case 4: return CS::BPM; + case 5: return CS::PPM; + default: return CS::FRAG; + } +} + +void Pane_playerinfo::draw() { + Pane_close::draw(); + if(game->net_list.size() == 0) + inter->font->draw(ST_NOPLAYERJOINED, screen, 19, 50); +} + +void Pane_playerinfo::step() { + int i,j; + Pane_close::step(); + ifdone(); + if(show_quoi!=o_show_val) { + o_show_val=show_quoi; + refresh(); + } + for(j=0; j<4; j++) // epure ceux 'tagged' mais deja affiche dans un autre pane (ou gone) + if(tagged[j] != -1) { + Canvas *c = game->net_list.get(tagged[j]); + if(c) { + if(c->inter != NULL || c->idle==3) + tag(tagged[j]); // de-selectionne (car deja affiche ou deconnecte) + } + else + tagged[j]=-1; + } + + if(!Pane::clicked) + if(!auto_watch) + return; + if(Pane::clicked && Pane::clicked == auto_button) { + if(auto_watch) + deactivate_auto_watch(); + else + activate_auto_watch(); + } + if(auto_watch) + clear_tag(); + + if((playback && playback->auto_demo) || !auto_watch) { + int auto_watch_team=-1; + for(i=0; inet_list.get(i); + if(c && ((player[i] && player[i]==Pane::clicked) || auto_watch)) { + if(!c->islocal() && c->inter == NULL) { + if(c->idle != 3) { + if(auto_watch_team==-1) + auto_watch_team=c->color; + if(playback && playback->auto_demo) { + if(auto_watch_team==c->color && game->delay_start!=500) + tag(i); + } + else + tag(i); + if(!auto_watch) //Un tag a la fois seulement si pas auto_watch + break; + } + } + } + } + } + else { + bool to_tag[MAXPLAYERS]; + int count=0; + for(i=0; inet_list.get(i); + if(c && c->idle<2 && !c->islocal() && c->inter == NULL) { + to_tag[i]=true; + count++; + } + else + to_tag[i]=false; + } + for(i=0; inet_list.get(i); + if(c && c->idle==2 && !c->islocal() && c->inter == NULL) { + to_tag[i]=true; + count++; + } + } + int team; + for(team=0; teamnet_list.get(i)->color==team) + tag(i); + } + if(Pane::clicked && Pane::clicked == show_button || auto_watch) { + if(Pane::clicked && Pane::clicked == show_button) + deactivate_auto_watch(); + int count = 0, solo = 0; + for(j=0; j<4; j++) + if(tagged[j] != -1) { + count++; + solo = tagged[j]; + } + + if(count == 1) { + hidecall(new Pane_startwatch(pi, solo, this)); // fullpane si 1 seul watch + } else { + for(int j=0; j<4; j++) + if(tagged[j] != -1) { + hidecall(new Pane_smallwatch(pi, tagged, this)); // demarre si plus que 1 tagged + break; + } + } + } +} + +void Pane_playerinfo::auto_watch_closed() { + activate_auto_watch(); +} + +bool Pane_playerinfo::auto_watch_started() { + bool t = auto_watch; + deactivate_auto_watch(); // met a OFF temporairement, au cas ou l'usager click sur "Fermer" + return t; +} + +Chat_interface::Zone_chat_input::Zone_chat_input(Chat_interface *p, const Palette &pal, Inter* in, char* s, int mlen, int px, int py, int pw): + Zone_text_input(in, pal, s, mlen, px, py, pw) { + parent = p; + kb_focusable = true; +} + +void Chat_interface::Zone_chat_input::lost_focus(int cancel) { + Zone_text_input::lost_focus(cancel); + if(!cancel && strlen(st) > 0) { + Packet_clientchat p; + if(parent->buf[0]=='/') { + //No player name when sending commands + strcpy(p.text, parent->buf); + p.team=-1; + p.to_team=-1; + } + else { + sprintf(p.text, "%s: %s", config.player[chat_text->quel_player].name, parent->buf); + p.team = config.player[chat_text->quel_player].color; + p.to_team = chat_text->to_player-1; + } + net->sendtcp(&p); + val[0] = 0; + set_val(val); + } +} + +Chat_interface::Zone_to_team::Zone_to_team(Inter *in, int *val, int px, int py): + Zone_state_text(in, val, px, py) { + add_string(ST_ALLTEAM); + for(int i=0; i 0) { + int team = *val - 1; + for(int i=0; inet_list.get(i); + if(c && c->color == team) { + vide = false; + break; + } + } + } else { + vide = false; + } + } while(vide); + + Sfx stmp(sons.enter, 0, -800, 0, 26000+ugs_random.rnd(1023)); +} + +Chat_interface::Chat_interface(Inter *in, const Palette &pal, Bitmap *bit, int px, int py, int pw, int ph, Video_bitmap *scr): Zone(in) { + buf[0] = 0; + if(!playback) { + zinput = new Zone_chat_input(this, pal, inter, buf, 200, px, 410, pw); + zone.add(zinput); + zone.add(new Zone_text(inter, ST_FROM, px, 435)); + + z_from = new Zone_state_text2(inter, &chat_text->quel_player, px+70, 435); + notify(); + zone.add(z_from); + zone.add(new Zone_text(inter, ST_TO, px, 455)); + + zone.add(new Zone_to_team(inter, &chat_text->to_player, px+70, 455)); + } else { + z_from = NULL; + zinput = NULL; + } + if(scr) { + set_screen_offset(0, scr); + delete_screen = false; + } else { + set_screen_offset(0, Video_bitmap::New(px, py, pw, ph)); + delete_screen = true; + } + back = new Bitmap((*bit)[py]+px, pw, 18*20, bit->realwidth); + if(game) + game->net_list.add_watch(this); +} + +Chat_interface::~Chat_interface() { + if(game) + game->net_list.remove_watch(this); + if(delete_screen) + delete screen; + delete back; +} + +void Chat_interface::set_screen_offset(int o, Video_bitmap *vb) { + y_offset = o; + screen = vb; + dirt(); +} + +void Chat_interface::draw() { + screen->setmem(); + back->draw(screen, 0, -y_offset); + int ty, i; + for(i=0; ilist[i].team != -1) + fcp = fteam[chat_text->list[i].team]; + else + fcp = inter->font; + scp = inter->font; + char tmp[256]; + strcpy(tmp, chat_text->list[i].text); + int color_cut=chat_text->list[i].color_cut; + if(color_cut!=-1) + tmp[color_cut]=0; + fcp->draw(tmp, screen, 1, ty); + if(color_cut!=-1) { + int x=fcp->width(tmp)+1; + scp->draw(&chat_text->list[i].text[color_cut], screen, x, ty); + } + } +/* if(y_offset != 0) { + screen->hline(0, 0, screen->width, 0); + screen->hline(1, 0, screen->width, 255); + screen->hline(2, 0, screen->width, 0); + }*/ +} + +void Chat_interface::process() { + Zone::process(); + if(chat_text->new_text) { + dirt(); + chat_text->new_text = false; + } + if(!playback) { + // detecte la touche 'ENTER' (en Doze et/ou en Ux) + if(input->quel_key == KEY_ENTER && !inter->focus && inter->focus != zinput) { + inter->select_zone(zinput, 0); + input->quel_key = -1; + } + } +} + +void Chat_interface::notify() { + if(z_from) { + z_from->nstate = 0; // impoli, mais fuck off + for(int i=0; i<3; i++) + z_from->add_string(config.player[i].name, fteam[config.player[i].color]); + } +} + +Pane_scoreboard::Pane_scoreboard(const Pane_info &p, bool control_button, bool dback, bool dbottom): Pane_close(p, dback, dbottom) { + old_size=0; + potato_team=255; + b_show_frag = NULL; + if(!game->single) { + if(!(playback && playback->auto_demo) && control_button) { + b_show_frag = new Zone_text_button2(pi.inter, pi.fond, pi.font2, "·4", pi.x+110, 10); + zone.add(b_show_frag); + } + } + game->net_list.add_watch(this); + score=game->net_list.score; + deactivate_frag(false); +} + +Pane_scoreboard::~Pane_scoreboard() { + game->net_list.remove_watch(this); +} + +void Pane_scoreboard::step() { + Pane_close::step(); + if(clicked && clicked==b_show_frag) { + show_frag = !show_frag; + if(show_frag) + activate_frag(); + else + deactivate_frag(false); + } +} + +void Pane_scoreboard::process() { + Pane_close::process(); + if(show_frag) { + if(!(overmind.framecount&127)) { + score.updateFromGame(); + if(score.team_order_changed || potato_team!=game->potato_team) { + //Order changed, refresh the whole thing + Pane_scoreboard::notify(); + } + } + } +} + +void Pane_scoreboard::activate_frag() { + char st[1024]; + st[0]=0; + score.updateFromGame(); + int team, team2; + if(b_show_frag) + b_show_frag->set_text("·3"); + show_frag=true; + int x2=x+11; + int y2=33, y_height=20; + + Zone_panel *zp = new Zone_panel(inter, x2-7, y2, w-11-25+14, 2); + + Zone *z; + if(game->game_end==END_FRAG || game->game_end==END_POINTS || game->game_end==END_LINES) { + const char *unit=NULL; + switch(game->net_list.goal_stat) { + case CS::FRAG: unit=ST_FRAG; break; + case CS::SCORE: unit=ST_POINT; break; + case CS::LINESTOT: unit=ST_LINE; break; + default: unit="frog"; break; + } + sprintf(st, ST_GOAL, unit, game->game_end_value!=1? "s":""); + z = new Zone_text(inter, st, x2, y2+2); + zone.add(z); + zlist_frag.add(z); + z = new Zone_text_field(inter, &game->game_end_value, x+w-95, y2+1, 70, pi.mp->courrier); + zone.add(z); + zlist_frag.add(z); + y2 += y_height; + } + for(team2=0; team2net_list.get(i); + if(c && c->color == team) { + nb++; + if(c->idle<3) { + nb_not_gone++; + tot_handicap+=c->handicap; + } + a_canvas = c; + } + } + if(nb > 0) { + if(game->hot_potato && team==game->potato_team) + strcpy(st, "·2 "); + else + st[0]=0; + if(nb==1) + strcat(st, a_canvas->name); + else + strcat(st, team_name[team]); + int avg_handicap=2; + if(nb_not_gone) + avg_handicap=tot_handicap/nb_not_gone; + char *handi=""; + switch(avg_handicap) { + case 0: handi=" (-)"; break; + case 1: handi=" (A)"; break; + case 3: handi=" (M)"; break; + case 4: handi=" (+)"; break; + } + strcat(st, handi); + if(nb==1) { + if(a_canvas->idle==3) + strcat(st, " *"); + } + else { + char st2[6]; + sprintf(st2, " (%i)", nb_not_gone); + strcat(st, st2); + } + int *statp; + z = new Zone_text(fteam[team], inter, st, x2, y2+2); + zone.add(z); + zlist_frag.add(z); + Dword width=70; + if(game->net_list.goal_stat==CS::FRAG) + width=33; + statp=score.team_stats[team].stats[game->net_list.goal_stat].get_address(); + z = new Zone_text_field(inter, statp, x+w-95, y2+1, width, pi.mp->courrier); + zone.add(z); + zlist_frag.add(z); + if(game->net_list.goal_stat==CS::FRAG) { + statp=score.team_stats[team].stats[CS::DEATH].get_address(); + z = new Zone_text_field(inter, statp, x+w-58, y2+1, width, pi.mp->courrier); + zone.add(z); + zlist_frag.add(z); + } + y2 += y_height; + } + } + if(y2 != 33) { + zone.add(zp); + zlist_frag.add(zp); + y2+=2; + zp->h = y2-33; + } + else { + delete zp; + y2=0; + } + size=y2; + if(size!=old_size) + video->need_paint = 2; + old_size=size; +} + +void Pane_scoreboard::deactivate_frag(bool temp) { + //temp: tells if we should deactivate only temporarily + // (see notify) + if(b_show_frag && !temp) + b_show_frag->set_text("·4"); + show_frag=false; + for(int i=0; ineed_paint = 2; + old_size=0; + } +} + +void Pane_scoreboard::notify() { + potato_team=game->potato_team; + if(show_frag) { + deactivate_frag(true); + activate_frag(); + } +} + +void Pane_scoreboard::scoreboard_invisible() { + if(b_show_frag) { + b_show_frag->disable(); + } +} + +Pane_chat::Pane_chat(const Pane_info &p): Pane_scoreboard(p, true, false, false) { + old_y=0; + int i; + for(i=0; i<3; i++) + ((Pane_option *) pi.mp->pane[i])->chat_window->disable(); + chat = new Chat_interface(inter, pi.mp->pal, pi.fond, x, y, w, h, screen); + zone.add(chat); + set_net_pane(3); + allow_clock(); + if(playback && !playback->auto_demo) { + zone.add(new Zone_slow_play(inter, pi.mp->bit, pi.mp->font2, ST_SLOWPLAY, x+2, 420)); + zone.add(new Zone_fast_play(inter, pi.mp->bit, pi.mp->font2, ST_FASTPLAY, x+2, 450)); + b_quit = new Zone_text_button2(inter, pi.mp->bit, pi.mp->font2, ST_BACK, x+132, 455); + zone.add(b_quit); + } + else { + b_quit=NULL; + } + activate_frag(); +} + +Pane_chat::~Pane_chat() { + for(int i=0; i<3; i++) + if(pi.mp->pane[i]) + ((Pane_option *) pi.mp->pane[i])->chat_window->enable(); +} + +void Pane_chat::activate_frag() { + Pane_scoreboard::activate_frag(); + if(size!=old_y) { + int diff = size-pi.y; + delete screen; + screen = Video_bitmap::New(pi.x, size, pi.w, pi.h-diff); + chat->set_screen_offset(diff, screen); + } + old_y=size; +} + +void Pane_chat::deactivate_frag(bool temp) { + Pane_scoreboard::deactivate_frag(temp); + if(!temp) { + delete screen; + screen = Video_bitmap::New(pi.x, pi.y, pi.w, pi.h); + chat->set_screen_offset(0, screen); + old_y=0; + } +} + +void Pane_chat::step() { + Pane_scoreboard::step(); + ifdone(); + if(clicked && clicked==b_quit) + pi.mp->stop=true; +} + +Pane_blockinfo::Pane_blockinfo(const Pane_info &p): + Pane_close(p, true, true) { + int i; + old_gauche = gauche = 0; + old_droite = droite = 1; + add_info(); + for(i=0; i<7; i++) + bloc[i] = new Bloc(i); + set_net_pane(4); + allow_clock(); + game->net_list.add_watch(this); +} + +Pane_blockinfo::~Pane_blockinfo() { + for(int i=0; i<7; i++) + delete bloc[i]; + game->net_list.remove_watch(this); +} + +void Pane_blockinfo::notify() { + deleteall(); + add_info(); + dirt(); +} + +void Pane_blockinfo::draw() { + Pane_close::draw(); + for(int i=0; i<7; i++) { + bloc[i]->draw(video->vb, px, 2+(2*18+2)*i + y); + } +} + +void Pane_blockinfo::add_info() { + Canvas *can=NULL, *can2=NULL; + if(game->net_list.size() > 1) { + Zone_state_text2 *temp = new Zone_state_text2(inter, &gauche, x+4, 420, 110); + zone.add(temp); + Zone_state_text2 *temp2 = new Zone_state_text2(inter, &droite, x+100, 445, 110); + zone.add(temp2); + int num = 0; + if(gauche >= game->net_list.size()) + gauche = game->net_list.size()-1; + old_gauche=gauche; + if(droite >= game->net_list.size()) + droite = game->net_list.size()-1; + old_droite=droite; + for(int i=0; inet_list.get(i); + if(c) { + temp->add_string(c->name, fteam[c->color]); + temp2->add_string(c->name, fteam[c->color]); + if(num == gauche) + can = c; + if(num == droite) + can2 = c; + num++; + } + } + } + if(game->net_list.size() == 1) { + for(int i=0; inet_list.get(i); + if(can) + break; + } + } + + if(can2) { + px = 65+x; + block_info(can, 2); + block_info(can2, 130); + } else { + px = 24+x; + if(can) + block_info(can, 90); + } + zone.add(new Zone_text(inter, ST_TOTAL, px+19, 300+y)); +} + +void Pane_blockinfo::step() { + Pane_close::step(); + ifdone(); + if(old_gauche!=gauche || old_droite!=droite) + notify(); +} + +void Pane_blockinfo::block_info(Canvas *can, int dx) { + for(int i=0; i<7; i++) { + zone.add(new Zone_text_field(inter, can->stats[CS::COMPTE0+i].get_address(), dx+x+9, 30+(2*18+2)*i+y, 50, pi.mp->courrier)); + } + zone.add(new Zone_text_field(inter, can->stats[CS::COMPTETOT].get_address(), dx+x+9, 300+y, 50, pi.mp->courrier)); +} + +Pane_comboinfo::Pane_comboinfo(const Pane_info &p): + Pane_close(p, true, true) { + old_gauche = gauche = 0; + old_droite = droite = 1; + game->net_list.add_watch(this); + + add_info(); + set_net_pane(5); + allow_clock(); +} + +Pane_comboinfo::~Pane_comboinfo() { + game->net_list.remove_watch(this); +} + +void Pane_comboinfo::step() { + Pane_close::step(); + ifdone(); + if(old_gauche!=gauche || old_droite!=droite) + notify(); +} + +void Pane_comboinfo::combo_info(Canvas *can, int dx) { + int py = 15+y; + for(int i=0; i<15; i++) { + zone.add(new Zone_text_field(inter, can->stats[CS::CLEAR00+i].get_address(), dx+x+9, py, 50, pi.mp->courrier)); + py += 20; + } + py += 10; + zone.add(new Zone_text_field(inter, can->stats[CS::LINESTOT].get_address(), dx+x+9, py, 50, pi.mp->courrier)); +} + +void Pane_comboinfo::notify() { + deleteall(); + add_info(); + dirt(); +} + +void Pane_comboinfo::add_info() { + Canvas *can=NULL, *can2=NULL; + int i; + if(game->net_list.size() > 1) { + Zone_state_text2 *temp = new Zone_state_text2(inter, &gauche, x+4, 420, 110); + Zone_state_text2 *temp2 = new Zone_state_text2(inter, &droite, x+100, 445, 110); + int num = 0; + if(gauche >= game->net_list.size()) + gauche = game->net_list.size()-1; + old_gauche=gauche; + if(droite >= game->net_list.size()) + droite = game->net_list.size()-1; + old_droite=droite; + for(int i=0; inet_list.get(i); + if(c) { + temp->add_string(c->name, fteam[c->color]); + temp2->add_string(c->name, fteam[c->color]); + if(num == gauche) + can = c; + if(num == droite) + can2 = c; + num++; + } + } + zone.add(temp); + zone.add(temp2); + } + if(game->net_list.size() == 1) { + old_gauche=gauche=old_droite=droite=0; + for(i=0; inet_list.get(i); + if(can) + break; + } + } + + i = y+15; + int px; + + if(can2) { + px = 65+x; + if(can) + combo_info(can, 2); + combo_info(can2, 130); + } else { + px = 24+x; + if(can) + combo_info(can, 90); + } + zone.add(new Zone_text(inter, ST_CLEARINFO1, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO2, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO3, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO4, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO5, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO6, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO7, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO8, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO9, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO10, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO11, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO12, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO13, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFO14, px, i)); i+=20; + zone.add(new Zone_text(inter, ST_CLEARINFOMORE, px, i)); i+=20; + i += 10; + zone.add(new Zone_text(inter, ST_TOTAL, px, i)); +} + +Pane_playerstartup::Pane_playerstartup(const Pane_info &p, int q): + Pane_close(p) { + int y=50; + qplayer = q; + + b_start = new Zone_text_button2(inter, pi.fond, pi.font2, ST_START, x+15, y, w-44); + zone.add(b_start); + y+=35; + if(game && game->allow_handicap) { + zone.add(new Zone_text(inter, ST_SELECTHANDICAP, x+9, y, w-30)); + y+=20; + handicap=config.player2[qplayer].handicap; + list_handicap = new Zone_state_text2(inter, &handicap, x+42, y); + list_handicap->add_string(ST_BEGINNER); + list_handicap->add_string(ST_APPRENTICE); + list_handicap->add_string(ST_INTERMEDIATE); + list_handicap->add_string(ST_MASTER); + list_handicap->add_string(ST_GRANDMASTER); + zone.add(list_handicap); + y+=35; + } + else { + handicap=2; + list_handicap=NULL; + } + zone.add(new Zone_text(inter, ST_SELECTTEAM, x+9, y, w-30)); + y+=20; + color = config.player[qplayer].color; + list_team = new Zone_state_text2(inter, &color, x+29, y); + int t; + for(t=0; tadd_string(team_name[t], fteam[t]); + zone.add(list_team); + Byte col[MAXTEAMS]; + for(t=0; tkb_focusable = false; + zone.add(color_team); + y+=35; + list_team->set_child(color_team); + + zone.add(new Zone_text(inter, ST_PLAYERINTEAM, x+9, y, w-30)); + y+=20; + list_player = new Zone_listbox2(inter, pi.fond, pi.font2, NULL, x+19, y, 160, 170); + zone.add(list_player); + game->net_list.add_watch(this); + update_player(); // force le 1er update +} + +Pane_playerstartup::~Pane_playerstartup() { + game->net_list.remove_watch(this); +} + +void Pane_playerstartup::notify() { + update_player(); +} + +void Pane_playerstartup::update_player() { + list_player->clear(); + for(int i=0; inet_list.get(i); + if(c) + if(c->color == list_team->last_val) + list_player->add_item(new Listable(c->name, fteam[c->color])); + } +} + +void Pane_playerstartup::step() { + Pane_close::step(); + if(clicked == list_team || clicked == color_team) { + update_player(); + } + if(clicked == b_start) { // si click start ou que joueur deja demarré (mais caché) + config.player[qplayer].color = color; // assigne la couleur selon le choix + config.player2[qplayer].handicap = handicap; + Module *m; + m = new Pane_playerjoin(pi, qplayer); + game->net_list.remove_watch(this); + hideexec(m); + } +} + +Pane_playerjoin::Pane_playerjoin(const Pane_info &p, int q): + Pane_close(p) { + Packet_playerwantjoin pjoin; + qplayer = q; + status = new Zone_text(inter, ST_WAITINGTOJOIN, x+9, 100, w-30); + zone.add(status); + pjoin.team=config.player[qplayer].color; + strcpy(pjoin.name, config.player[qplayer].name); + if(config.player2[qplayer].ngPasswd[0]) { + Unicode uni_p(pjoin.name); + uni_p.cat(config.player2[qplayer].ngPasswd); + Crypt name_crypt; + name_crypt.step(uni_p, uni_p.size()); + name_crypt.finalize(false); + memcpy(pjoin.player_hash, name_crypt.get_digest(), 16); + } + else + memset(pjoin.player_hash, 0, 16); + pjoin.player=qplayer; + pjoin.shadow=config.player[qplayer].shadow; + pjoin.smooth=config.player[qplayer].smooth; + pjoin.h_repeat=config.player2[qplayer].h_repeat; + pjoin.v_repeat=config.player2[qplayer].v_repeat; + pjoin.handicap=config.player2[qplayer].handicap; + strcpy(pjoin.team_name, config.player2[qplayer].ngTeam); + if(pjoin.team_name[0] && config.player2[qplayer].ngTeamPasswd[0]) { + Unicode uni_t(pjoin.team_name); + uni_t.cat(config.player2[qplayer].ngTeamPasswd); + Crypt team_crypt; + team_crypt.step(uni_t, uni_t.size()); + memcpy(pjoin.team_hash, team_crypt.get_digest(), 16); + } + else + memset(pjoin.team_hash, 0, 16); + eping=new Exec_ping(&pjoin, P_PLAYERACCEPTED, this); + got_answer=false; + if(close) + close->disable(); +} + +Pane_playerjoin::~Pane_playerjoin() { + if(eping) + delete eping; +} + +void Pane_playerjoin::step() { + Pane_close::step(); + //Re-enable close when we get an answer + if(got_answer && close) { + //Return got_answer to false so we don't enable close multiple + // times (that's an evil thing to do). If a fake server + // sends more than one answer, we're screwed but then again, + // fake servers can screw us in 1000s of ways already... + got_answer=false; + close->enable(); + } +} + +void Pane_playerjoin::net_call(Packet *p2) { + got_answer=true; + Packet_playeraccepted *p=(Packet_playeraccepted *) p2; + if(p->accepted == 0) { // si le join a ete accepte + hideexec(new Pane_startgame(pi, qplayer, NULL, p->pos)); + } else if(p->accepted == 4) { // si ya deja un joueur 'gone' et qu'on le remplace + hideexec(new Pane_startgame(pi, qplayer, game->net_list.get(p->pos), p->pos)); + } else { + const char *s1 = NULL, *s2 = NULL; + switch(p->accepted) { + case 1: // serveur refuse tous les joins + s1 = ST_PLAYERJOINREFUSED; + s2 = ST_PLAYERJOINREFUSED2; + break; + case 2: // deja qqun avec ce nom la + s1 = ST_PLAYERJOINALREADY; + s2 = ST_PLAYERJOINALREADY2; + break; + case 3: // partie terminee, pas de join + s1 = ST_PLAYERJOINREFUSED3; + s2 = ST_PLAYERJOINREFUSED4; + break; + case 5: // partie pleine. (MAX_PLAYERS) + s1 = ST_PLAYERJOINFULL1; + s2 = ST_PLAYERJOINFULL2; + break; + }; + dirt(); + status->set_text(s1); + zone.add(new Zone_text(inter, s2, x+9, 130, w-30)); + } +} + +Pane_startgame::Pane_startgame(const Pane_info &p, int q, Canvas *c, int pos): + Pane_close(p, false) { + delete_zone = true; + qplayer = q; + canvas = c; + if(canvas == NULL) { // si pas deja la, ajoute nouveau joueur + canvas = new Canvas(qplayer, game->seed, &pi.mp->pal); + if(pos==-1) { + //Pour game locale + game->net_list.add_player(canvas); + } + else { + //Pour game internet + game->net_list.set_player(canvas, pos, true); + } + } + else { // si resume ou affiche en plein ecran + if(qplayer!=-1) { // si resume un joueur local + canvas->clear_key_all(); + if(canvas->idle == 3) + delete_zone = false; // empeche le refresh qui indiquera que ce joueur rejoin car cela delete les zones + } + } + //What the fuck does a canvas need a Palette and Bitmap for, + // anyway? + canvas->pal = &pi.mp->pal; + canvas->bit = pi.fond; + if(qplayer!=-1) { + for(int i=0; i<5; i++) + pi.inter->kb_alloc_key(config.player[qplayer].key[i]); + pi.inter->kb_alloc_key(config.player2[qplayer].key[0]); + pi.inter->kb_alloc_key(config.player2[qplayer].key[1]); + } + + num_player = canvas->num_player; + if(delete_zone) + create_zone(); + game->net_list.add_watch(this); +} + +Pane_startgame::~Pane_startgame() { + if(canvas) + canvas->hide(); + game->net_list.remove_watch(this); + if(qplayer!=-1) { + for(int i=0; i<5; i++) + pi.inter->kb_free_key(config.player[qplayer].key[i]); + pi.inter->kb_free_key(config.player2[qplayer].key[0]); + pi.inter->kb_free_key(config.player2[qplayer].key[1]); + } +} + +void Pane_startgame::create_zone() { + zone.add(new Zone_canvas(inter, *pi.fond, x+9, 37, canvas)); + zone.add(new Zone_canvas_bloc(inter, canvas)); + zone.add(new Zone_text(inter, ST_GAMESCORE, x+9 - 5, 403)); + zone.add(new Zone_text_field(inter, canvas->stats[CS::SCORE].get_address(), x+9+55, 403, 120, pi.mp->courrier)); + zone.add(new Zone_text(inter, ST_GAMELINES, x+9 - 5, 422)); + canvas->z_lines=new Zone_text_field(inter, &canvas->lines, x+9+55, 422, 65, pi.mp->courrier); + canvas->z_potatolines=new Zone_text_field(inter, &canvas->team_potato_lines, x+9+55, 422, 65, pi.mp->courrier); + if(canvas->color==game->potato_team) + canvas->z_lines->disable(); + else + canvas->z_potatolines->disable(); + zone.add(canvas->z_lines); + zone.add(canvas->z_potatolines); + zone.add(new Zone_text(inter, ST_GAMELEVEL, x+9 - 5, 460)); + zone.add(new Zone_text_field(inter, &canvas->level, x+9+55, 460, 35, pi.mp->courrier)); + char *the_guys_name=canvas->name; + if(!game->single) + the_guys_name=canvas->long_name(true, false); + Zone_text *name = new Zone_text(inter, the_guys_name, x+9 + 95, 460); + if(!game->single) + name->set_font(fteam[canvas->color]); + zone.add(name); + if(!game->single) { + zone.add(new Zone_text(inter, ST_GAMEFRAGS, x+9 - 5, 441)); + zone.add(new Zone_text_field(inter, canvas->stats[CS::FRAG].get_address(), x+9+55, 441, 35, pi.mp->courrier)); + zone.add(new Zone_text(inter, ST_GAMEDEATHS, x+9 + 95, 441)); + zone.add(new Zone_text_field(inter, canvas->stats[CS::DEATH].get_address(), x+9+155, 441, 35, pi.mp->courrier)); + canvas->z_linestot=new Zone_text_field(inter, canvas->stats[CS::LINESTOT].get_address(), x+9+55+70, 422, 65, pi.mp->courrier); + canvas->z_potatolinestot=new Zone_text_field(inter, &canvas->team_potato_linestot, x+9+55+70, 422, 65, pi.mp->courrier); + if(canvas->color==game->potato_team) + canvas->z_linestot->disable(); + else + canvas->z_potatolinestot->disable(); + zone.add(canvas->z_linestot); + zone.add(canvas->z_potatolinestot); + } +} + +void Pane_startgame::step() { + Pane_close::step(); +// if(done) +// canvas->hide(); +} + +void Pane_startgame::notify() { + if(game->net_list.get(num_player) == NULL) { + if(delete_zone) { + msgbox("Pane_startgame::notify: Indeed, player %i was dropped.\n", num_player); + zone.deleteall(); + canvas = NULL; + ret(); + } else { + msgbox("Pane_startgame::notify: Player %i is re-joining and becoming a local canvas.\n", num_player); + canvas->pal = &pi.mp->pal; + canvas->local_player = true; + canvas->remote_adr = NULL; + canvas->player = qplayer; + delete_zone = true; + create_zone(); + } + } +} + +Pane_startwatch::Pane_startwatch(const Pane_info &p, int player, Pane_playerinfo *ppinfo): + Pane_startgame(p, -1, game->net_list.get(player)) { + pinfo = ppinfo; + auto_watch=pinfo->auto_watch_started(); + watch = new Watch_canvas(player); + watch->start(); +} + +Pane_startwatch::~Pane_startwatch() { + delete watch; +} + +void Pane_startwatch::step() { + Pane_startgame::step(); + if(done) { + watch->stop(); + } +} + +void Pane_startwatch::notify() { + Pane_startgame::notify(); + if(auto_watch) { // si 1 joueur est dropper ou un nouveau arrive, ferme + ret(); // car auto_watch va peut etre ouvrir en small watch (ou reouvrir le meme...) + pinfo->auto_watch_closed(); + } +} + +Pane_smallwatch::Pane_smallwatch(const Pane_info &p, int tagged[], Pane_playerinfo *ppinfo): + Pane_scoreboard(p, true) { + pinfo = ppinfo; + auto_watch=pinfo->auto_watch_started(); + int i; + for(i=0; i<4; i++) + watch[i] = NULL; + int nb_temp = 0; + for(i=0; i<4; i++) + if(tagged[i] != -1) + nb_temp++; + + compte = 0; + for(i=0; i<4; i++) { + if(tagged[i] != -1) { + int tx, ty; + if(compte == 0 || compte == 2) + tx = 16; + else + tx = 114; + if(compte == 0 || compte == 1) + ty = 219; + else + ty = 35; + watch[compte] = new Watch_canvas(tagged[i], true); + watch[compte]->start(); + watch[compte]->small_canvas(pi, tx, ty); + compte++; + } + } + if(compte==1 || compte==2) { + activate_frag(); + } else { + deactivate_frag(false); + scoreboard_invisible(); + } + allow_clock(); +} + +Pane_smallwatch::~Pane_smallwatch() { + for(int i=0; i<4; i++) + if(watch[i]) { + watch[i]->stop(); + watch[i]->c->hide(); + delete watch[i]; + } +} + +void Pane_smallwatch::notify() { + Pane_scoreboard::notify(); + bool deleted=false; + compte = 0; + for(int i=0; i<4; i++) + if(watch[i]) { + Canvas *c=game->net_list.get(watch[i]->play); + if(!c || c->idle==3) { + msgbox("Pane_smallwatch::notify: Indeed, player %i is gone.\n", watch[i]->play); + watch[i]->stop(); + delete watch[i]; + watch[i] = NULL; + deleted=true; + dirt(); + } + else + compte++; + } + if(deleted || compte == 0 || auto_watch) { // si pu aucun small_watch d'ouvert + ret(); + pinfo->auto_watch_closed(); + } + msgbox("Pane_smallwatch::notify: done\n"); +} + +void Pane_smallwatch::draw() { + Pane_scoreboard::draw(); + for(int q=0; q<4; q++) { + if(watch[q]) { + int x = watch[q]->x; + int y = watch[q]->y; + screen->hline(y-1, x, 68, 255); + screen->vline(x-1, y, 120, 255); + screen->hline(y+120, x, 68, 255); + screen->vline(x+60, y, 120, 255); + screen->vline(x+67, y, 120, 255); + } + } +} + +Watch_canvas::Watch_canvas(int player, bool s) { + play = player; + small_watch = s; + c = game->net_list.get(play); +} + +void Watch_canvas::stop() { + Packet_clientstartwatch pac; + pac.player = play; + pac.stop = true; + net->sendtcp(&pac); +} + +void Watch_canvas::start() { + Packet_clientstartwatch pac; + pac.player = play; + pac.stop = false; + pac.update = config.info.update_rate; + if(small_watch) + pac.update = pac.update>>1; + net->sendtcp(&pac); +} + +void Watch_canvas::small_canvas(const Pane_info &pi, int tx, int ty) { + x = tx; + y = ty; + tx += pi.x; + ty += pi.y; + zone.add(new Zone_small_canvas(pi.inter, *pi.fond, tx, ty, c)); + zone.add(new Zone_small_canvas_bloc(pi.inter, c)); + Zone_text *name = new Zone_text(pi.inter, c->long_name(true, false), tx, ty+122, 66); + name->set_font(fteam[c->color]); + zone.add(name); +} diff --git a/source/player.cpp b/source/player.cpp new file mode 100644 index 0000000..08d05c6 --- /dev/null +++ b/source/player.cpp @@ -0,0 +1,12 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "player.h" + +Cok::Cok(Dword a) { + in_uid = out_uid = 0; + adr = a; +} diff --git a/source/qserv.cpp b/source/qserv.cpp new file mode 100644 index 0000000..205eed3 --- /dev/null +++ b/source/qserv.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include +#include +#include "config.h" +#include "url.h" +#include "http_post.h" +#include "dict.h" +#include "stringtable.h" +#include "video.h" +#include "qserv.h" + +Dword Qserv::http_addr=0; +int Qserv::http_port=0; + +Qserv::Qserv() { + req=NULL; + status[0]=0; + reply=NULL; + Url url(config.info.game_server_address); + if(!url.getPort()) + url.setPort(80); + if(!strcmp(url.getHost(), "")) + url.setHost("ludusdesign.com:80"); + if(!strcmp(url.getPath(), "")) + url.setPath("/cgibin/qserv.pl"); + if(http_addr) + req=new Http_post(http_addr, http_port, url.getPath()); + else + req=new Http_post(url.getHost(), url.getPort(), url.getPath()); + req->add_data_raw("data="); +} + +Qserv::~Qserv() { + if(req) + delete req; + if(reply) + delete reply; +} + +bool Qserv::done() { + if(!req) + return true; + if(!req->done()) + return false; + + //Save ip info for future requests + Qserv::http_addr = req->gethostaddr(); + Qserv::http_port = req->gethostport(); + + //Parse reply + reply=new Dict(); + if(!req->getsize()) { + strcpy(status, ""); + } + else { + Stringtable st(req->getbuf(), req->getsize()); + int i=0; + for(i=0; i=st.size()) + strcpy(status, ""); + else { + strncpy(status, st.get(i), sizeof(status)-1); + status[sizeof(status)-1]=0; + i++; // Skip status line + } + break; + } + while(iadd(st.get(i)); + i++; + } + } + delete req; + req=NULL; + msgbox("Qserv::done: done\n"); + return true; +} + +void Qserv::add_data(const char *s, ...) { + char st[32768]; + Textbuf buf; + va_list marker; + va_start(marker, s); + vsprintf(st, s, marker); + va_end(marker); + Http_request::url_encode(st, buf); + req->add_data_raw(buf.get()); +} + +void Qserv::send() { + req->add_data_encode("info/language %i\n", config.info.language); + if(config.registered) + req->add_data_encode("info/registered %i\n", Config::registered? 1:0); + req->add_data_encode("info/quadra_version %i.%i.%i\n", config.major, config.minor, config.patchlevel); + req->add_data_encode("info/platform/os %s\n", + #if defined(UGS_DIRECTX) + "Windows" + #elif defined(UGS_LINUX) + "Linux i386" + #else + #error "What platform???" + #endif + ); + if(video_is_dumb) + req->add_data_encode("info/platform/display None\n"); + else { + #if defined(UGS_LINUX) + req->add_data_encode("info/platform/display %s\n", video->xwindow ? "Xlib":"Svgalib"); + #endif + #if defined(UGS_DIRECTX) + req->add_data_encode("info/platform/display DirectX\n"); + #endif + } + req->send(); +} + +const char *Qserv::get_status() { + if(status[0]) + return status; + else + return NULL; +} + +Dict *Qserv::get_reply() { + return reply; +} + +bool Qserv::isconnected() const { + if(req && req->isconnected()) + return true; + return false; +} + +Dword Qserv::getnbrecv() const { + int val = 0; + if(req) { + val = req->getsize(); + if(val < 0) + val = 0; + } + return val; +} diff --git a/source/quadra.cpp b/source/quadra.cpp new file mode 100644 index 0000000..10e1e93 --- /dev/null +++ b/source/quadra.cpp @@ -0,0 +1,2401 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + + +#ifdef UGS_LINUX +#include +#include +#include +#define stricmp strcasecmp +#endif +#include +#include "packet.h" +#include "types.h" +#include "net.h" +#include "video.h" +#include "cursor.h" +#include "palette.h" +#include "input.h" +#include "sound.h" +#include "music.h" +#include "sprite.h" +#include "bitmap.h" +#include "inter.h" +#include "random.h" +#include "game.h" +#include "bloc.h" +#include "color.h" +#include "menu.h" +#include "main.h" +#include "overmind.h" +#include "command.h" +#include "multi_player.h" +#include "stringtable.h" +#include "net_stuff.h" +#include "chat_text.h" +#include "recording.h" +#include "texte.h" +#include "canvas.h" +#include "global.h" +#include "sons.h" +#include "config.h" +#include "fonts.h" +#include "res_compress.h" +#include "highscores.h" +#include "crypt.h" +#include "unicode.h" +#include "nglog.h" +#include "clock.h" +#include "quadra.h" + +Sprite *cur; + +Color *color[9]; +Font *fteam[8]; + +void set_fteam_color(const Palette& pal) { + fteam[0]->colorize(pal, 255,125,0); + fteam[1]->colorize(pal, 0,225,255); + fteam[2]->colorize(pal, 255,0,0); + fteam[3]->colorize(pal, 255,0,255); + fteam[4]->colorize(pal, 255,255,0); + fteam[5]->colorize(pal, 0,255,0); + fteam[6]->colorize(pal, 40,40,255); + fteam[7]->colorize(pal, 170,170,170); +} + +void raw_draw_bloc_corner(const Video_bitmap* bit, int x, int y, Byte side, Color* col, Byte to[4]) { + raw_draw_bloc(bit, x, y, side, col); + if(!(side&1) && !(side&2) && to[0]&2 && to[1]&1) { + bit->put_pel(x, y, col->shade(7)); + bit->put_pel(x+1, y, col->shade(6)); + bit->put_pel(x, y+1, col->shade(6)); + bit->put_pel(x+2, y, col->shade(5)); + bit->put_pel(x+1, y+1, col->shade(5)); + bit->put_pel(x, y+2, col->shade(5)); + } + if(!(side&4) && !(side&2) && to[2]&2 && to[1]&4) { + bit->put_pel(x+17, y, col->shade(1+2)); + bit->put_pel(x+16, y, col->shade(2+1)); + bit->put_pel(x+17, y+1, col->shade(2+2)); + bit->put_pel(x+15, y, col->shade(3)); + bit->put_pel(x+16, y+1, col->shade(3+1)); + bit->put_pel(x+17, y+2, col->shade(3+2)); + } + if(!(side&1) && !(side&8) && to[0]&8 && to[3]&1) { + bit->put_pel(x, y+17, col->shade(7)); + bit->put_pel(x+1, y+17, col->shade(6)); + bit->put_pel(x, y+16, col->shade(6)); + bit->put_pel(x+2, y+17, col->shade(5)); + bit->put_pel(x+1, y+16, col->shade(5)); + bit->put_pel(x, y+15, col->shade(5)); + } + if(!(side&4) && !(side&8) && to[2]&8 && to[3]&4) { + bit->put_pel(x+17, y+17, col->shade(1)); + bit->put_pel(x+16, y+17, col->shade(2)); + bit->put_pel(x+17, y+16, col->shade(2)); + bit->put_pel(x+15, y+17, col->shade(3)); + bit->put_pel(x+16, y+16, col->shade(3)); + bit->put_pel(x+17, y+15, col->shade(3)); + } +} + +void raw_draw_bloc(const Video_bitmap* bit, int x, int y, Byte side, Color* col) { + int tx,tl,rx=0,ry=0,rw=18,rh=18; + if(side&1) { + bit->vline(x, y, 18, col->shade(7)); + bit->vline(x+1, y, 18, col->shade(6)); + bit->vline(x+2, y, 18, col->shade(5)); + rx=3; rw-=3; + } + if(side&2) { + bit->hline(y, x, 18, col->shade(7)); + if(side&1) + tx = x+1; + else + tx = x; + bit->hline(y+1, tx, x-tx+18, col->shade(6)); + if(side&1) + tx = x+2; + else + tx = x; + bit->hline(y+2, tx, x-tx+18, col->shade(5)); + ry=3; rh-=3; + } + if(side&4) { + bit->vline(x+17, y, 18, col->shade(1)); + if(side&2) + tx = y+1; + else + tx = y; + bit->vline(x+16, tx, y-tx+18, col->shade(2)); + if(side&2) + tx = y+2; + else + tx = y; + bit->vline(x+15, tx, y-tx+18, col->shade(3)); + rw-=3; + } + if(side&8) { + bit->hline(y+17, x, 18, col->shade(1)); + if(side&1) + tx = x+1; + else + tx = x; + tl = x-tx+18; + if(side&4) + tl--; + bit->hline(y+16, tx, tl, col->shade(2)); + if(side&1) + tx = x+2; + else + tx = x; + tl = x-tx+18; + if(side&4) + tl -= 2; + bit->hline(y+15, tx, tl, col->shade(3)); + rh-=3; + } + Byte main_color=col->shade(4); + for(int i=0; ihline(y+ry+i, x+rx, rw, main_color); +} + +void raw_small_draw_bloc(const Video_bitmap* bit, int x, int y, Byte side, Color* col) { + int i,rx=0,ry=0,rw=6,rh=6; + if(side&1) { + bit->vline(x, y, 6, col->shade(7)); + rx++; rw--; + } + if(side&2) { + bit->hline(y, x, 6, col->shade(7)); + ry++; rh--; + } + if(side&4) { + bit->vline(x+5, y, 6, col->shade(1)); + rw--; + } + if(side&8) { + bit->hline(y+5, x, 6, col->shade(1)); + rh--; + } + for(i=0; ihline(y+ry+i, x+rx, rw, col->shade(4)); +} + +Player_check_link::Player_check_link(Canvas *c): Player_base(c) { + anim = 0; + tombe = 0; + //Reset Canvas::moved + int i, j; + for(j=0; j<36; j++) + for(i=0; i<18; i++) + canvas->moved[j][i]=false; +} + +void Player_check_link::step() { + Player_base::step(); + int i, j, k = 1; + if(anim == 0) { + while(k) { // rescan plusieurs pass + k = 0; + for(j = 31; j >= 0; j--) { // pour chaque rangee : + for(i = 4; i < 14; i++) // scan gauche a droite -> + if((canvas->block[j][i]&8) && (!canvas->tmp[j][i]) && canvas->tmp[j+1][i]) { + fill_bloc(i,j); + k = 1; + } + } + } + anim++; + } else { + anim = 0; + k = 0; + //Temp move array + bool moved[36][18]; + for(j=0; j<36; j++) + for(i=0; i<18; i++) + moved[j][i]=false; + for(j = 31; j >= 0; j--) + for(i = 4; i < 14; i++) + if(canvas->occupied[j][i] && (!canvas->tmp[j][i])) { + k = 1; + canvas->block[j+1][i] = canvas->block[j][i]; + canvas->occupied[j+1][i] = canvas->occupied[j][i]; + moved[j+1][i] = true; + canvas->blinded[j+1][i] = canvas->blinded[j][i]; + canvas->bflash[j+1][i] = canvas->bflash[j][i]; + canvas->dirted[j+1][i] = 2; + canvas->block[j][i] = 0; + canvas->occupied[j][i] = false; + canvas->blinded[j][i] = 0; + canvas->bflash[j][i] = 0; + canvas->dirted[j][i] = 2; + } + if(k) { + tombe++; + //Some blocks moved, overwrite Canvas::moved + for(j=0; j<36; j++) { + bool movement_in_line=false; + for(i=0; i<18; i++) + if(moved[j][i]) + movement_in_line=true; + if(movement_in_line) + for(i=0; i<18; i++) + canvas->moved[j][i]=moved[j][i]; + } + } + if(!k) { + if(tombe) { + i = -500 + tombe * 50; + if(!canvas->islocal()) + i -= 1000; + if(i>0) + i=0; + if(canvas->inter) { + play_sound(sons.depose4, i, -1, 11000+ugs_random.rnd(127) - (canvas->complexity<<8)); + } + } + ret();//exec(new Player_check_line(canvas)); + } + } +} + +void Player_check_link::fill_bloc(Byte x, Byte y) { + canvas->tmp[y][x] = 1; + if((!(canvas->block[y][x]&8)) && (!canvas->tmp[y+1][x])) + fill_bloc(x, y+1); + if((!(canvas->block[y][x]&1)) && (!canvas->tmp[y][x-1])) + fill_bloc(x-1, y); + if((!(canvas->block[y][x]&4)) && (!canvas->tmp[y][x+1])) + fill_bloc(x+1, y); + if((!(canvas->block[y][x]&2)) && (!canvas->tmp[y-1][x])) + fill_bloc(x, y-1); +} + +Player_flash_lines::Player_flash_lines(Canvas *c): Player_base(c) { + anim = 0; + canvas->color_flash = 255; + if(canvas->inter) { + int vo=-300-ugs_random.rnd(255); + if(!canvas->islocal()) + vo -= 1000; + play_sound(sons.flash, vo, -1, 11000+ugs_random.rnd(127) - (canvas->complexity<<8)); + } +} + +void Player_flash_lines::step() { + Player_base::step(); + int i; + if(anim < 16) { + if((anim>>1)&1) + canvas->color_flash = 200; + else + canvas->color_flash = 255; + anim++; + } else { + canvas->color_flash = 0; + canvas->clear_tmp(); + anim = 0; + for(i = 0; i < 20; i++) + canvas->flash[i] = 0; + exec(new Player_check_link(canvas)); + } +} + +void Player_base::step() { + if(canvas->idle<3) + canvas->gone_time=0; + if(game && game->net_version()<23 && !game->paused && (canvas->idle==0 || canvas->idle==1)) + canvas->stats[CS::PLAYING_TIME].add(1); + if(!((overmind.framecount+1)&127)) { + int pm=0; + int time=canvas->stats[CS::PLAYING_TIME].get_value(); + if(time) + pm=(int)(canvas->stats[CS::SCORE].get_value()*6000.0/time); + canvas->stats[CS::PPM].set_value(pm); + pm=0; + if(time) + pm=(int)(canvas->stats[CS::COMPTETOT].get_value()*6000.0/time); + canvas->stats[CS::BPM].set_value(pm); + } + if(game && !game->paused && !(overmind.framecount&15)) { + int i, j; + for(j=0; j<36; j++) + for(i=0; i<18; i++) + if(canvas->blinded[j][i]) { + if(!--canvas->blinded[j][i]) { + canvas->bflash[j][i]=24; + canvas->dirted[j][i]=2; + } + } + } +} + +Byte Player_base::calc_by(int py) const { + return (((py+15+(12*18<<4))>>4)+17)/18; +} + +bool Player_base::check_gone() { + { + Packet_gone *p=(Packet_gone *) game->peekpacket(P_GONE); + if(p && p->player==canvas->num_player) { + bool chat_msg=p->chat_msg; + game->removepacket(); + msgbox("Player_base::check_gone: Player %i is gone\n", canvas->num_player); + exec(new Player_gone(canvas, chat_msg)); + ret(); + return true; + } + } + if(game->abort || game->terminated) { + if(canvas->islocal()) { + msgbox("Player_base::check_gone: local player %i gone\n", canvas->num_player); + Player_base *pb; + if(game->terminated || game->server || canvas->idle==2 || canvas->dying || game->delay_start==500) { + Packet_clientgone p; + p.player=canvas->num_player; + p.chat_msg=!game->terminated; + net->sendtcp(&p); + pb = new Player_gone(canvas, !game->terminated); + } + else { + //Player_dead::Player_dead sends the packet + pb = new Player_dead(canvas, true); + } + exec(pb); + ret(); + return true; + } + } + return false; +} + +void Player_base::check_state() { + Packet_state *p=(Packet_state *) game->peekpacket(P_STATE); + if(p && p->player==canvas->num_player) { + msgbox("Player_base::check_state: player=%i, state=%i\n", p->player, p->state); + canvas->state=(Canvas::State) p->state; + game->removepacket(); + } +} + +void Player_base::remove_bonus() { + canvas->should_remove_bonus=false; + canvas->bonus=0; + if(canvas->islocal()) { + Packet_clientremovebonus p; + p.player=canvas->num_player; + net->sendtcp(&p); + } +} + +void Player_base::check_bonus() { + Packet_removebonus *p=(Packet_removebonus *) game->peekpacket(P_REMOVEBONUS); + if(p && p->player==canvas->num_player) { + remove_bonus(); + game->removepacket(); + } +} + +void Player_base::play_sound(Sample *s, int vol, int pan, int freq) { + if(time_control == TIME_SLOW) + freq = freq*2/3; + if(time_control == TIME_FAST) + freq = freq*3/2; + Sfx stmp(s, 0, vol, pan, freq); +} + +Player_text_scroll::Player_text_scroll(Canvas *c, const char *texte, int xoffset, int yoffset): Player_base(c) { + (void)new Zone_combo(&combo, canvas, texte, canvas->x + xoffset, canvas->y + 330 + yoffset); + if(game) + game->net_list.add_watch(this); +} + +Player_text_scroll::~Player_text_scroll() { + if(game) // si ALT-F4: game est detruit avant ce module... grrr + game->net_list.remove_watch(this); +} + +void Player_text_scroll::notify() { + if(!game->net_list.get(canvas->num_player)) { + stop(); + } +} + +void Player_text_scroll::step() { + if(combo) { + combo->y-=2; + if(combo->y < canvas->y || canvas->inter==NULL || canvas->small_watch) { + stop(); + } + } +} + +void Player_text_scroll::stop() { + if(combo) { + delete combo; + combo=NULL; + } + ret(); +} + +Player_add_bonus::Player_add_bonus(Canvas *c): Player_base(c) { + anim = 0; + first_done=false; + if(canvas->inter) { + int vo = -600; + if(!canvas->islocal()) + vo -= 1000; + play_sound(sons.bonus1, vo, -1, 11000+ugs_random.rnd(511)); + } + nb=canvas->bonus; +} + +Player_add_bonus::~Player_add_bonus() { +} + +void Player_add_bonus::step() { + Player_base::step(); + int y, i, j; + bool old_net_version=false; + if(game->net_version()<23) + old_net_version=true; + if(nb && canvas->bonus) { + if(anim == 0) { + for(j = 0; j < 31; j++) + for(i = 4; i < 14; i++) { + canvas->block[j][i] = canvas->block[j+1][i]; + canvas->occupied[j][i] = canvas->occupied[j+1][i]; + canvas->blinded[j][i] = canvas->blinded[j+1][i]; + canvas->bflash[j][i] = canvas->bflash[j+1][i]; + canvas->dirted[j+1][i] = 2; + } + Word hole_pos=canvas->bon[0].hole_pos; + if(old_net_version) + hole_pos=(1 << (9-(canvas->bon[0].x-4))); + Byte top_bottom_add=0; + if(!first_done || old_net_version) + top_bottom_add |= 2; //Add top + if(canvas->bon[0].final || old_net_version) + top_bottom_add |= 8; //Add bottom + for(i = 4; i < 14; i++) { + canvas->block[31][i] = top_bottom_add + (canvas->bon[0].color<<4); + canvas->occupied[31][i] = true; + } + for(i = 4; i < 14; i++) { + if(hole_pos&512) { + canvas->block[31][i] = 0; + canvas->occupied[31][i] = false; + if(first_done && canvas->occupied[30][i]) { + //First line done in this packet and corresponding + // unit block above is not a hole: add bottom + canvas->block[30][i] |= 8; + } + if(i > 4) { + if(canvas->occupied[31][i-1]) + canvas->block[31][i-1] |= 4; + if(canvas->occupied[31][4]) + canvas->block[31][4] |= 1; + } + if(i < 14) { + canvas->block[31][i+1] |= 1; + canvas->block[31][14] |= 4; + } + } + else { + if(first_done && !canvas->occupied[30][i]) { + //First line done in this packet and corresponding + // unit block above is a hole: add top + canvas->block[31][i] |= 2; + } + } + canvas->blinded[31][i] = 0; + canvas->bflash[31][i] = 0; + hole_pos <<= 1; + } + first_done=true; + if(canvas->bon[0].final) + first_done=false; + } + anim++; + if(anim == 7) { + for(y = 1; y < canvas->bonus; y++) + canvas->bon[y-1] = canvas->bon[y]; + canvas->bonus--; + nb--; + anim=0; + } + } else { + for(i = 4; i < 14; i++) + if(canvas->occupied[0][i]) + canvas->block[0][i] |= 2; // clip top + canvas->clear_tmp(); + call(new Player_check_link(canvas)); + ret(); + } +} + +Player_change_level::Player_change_level(Canvas *c): Player_base(c) { + canvas->myself->pause(); +} + +Player_change_level::~Player_change_level() { + canvas->myself->unpause(); +} + +void Player_change_level::init() { + Player_base::init(); + canvas->change_level_single(); + if(canvas->inter) { + canvas->inter->font->colorize(*canvas->pal, 255, 255, 255); + video->need_paint = 2; + } + int nframe=16; + if(time_control == TIME_SLOW) + nframe = nframe*3/2; + if(time_control == TIME_FAST) + nframe = 3; + call(new Fade_to(*canvas->pal, noir, nframe)); + call(new Wait_time(14)); // juste pour eviter de voir le 'level up' boker + ret(); +} + +Player_level_up::Player_level_up(Canvas *c): Player_base(c) { + canvas->level_up = 220; + if(canvas->islocal() || (canvas->inter && !canvas->small_watch)) + Player_base::play_sound(sons.levelup, -200-ugs_random.rnd(127), 0, 11000); +} + +void Player_level_up::init() { + Player_base::init(); + if(game->single) { // && (canvas->level-1<=9 || game->net_version()<23)) { + call(new Player_change_level(canvas)); + int nframe=16; + if(time_control == TIME_SLOW) + nframe = nframe*3/2; + if(time_control == TIME_FAST) + nframe = 3; + call(new Fade_to(noir, *canvas->pal, nframe)); + } +} + +void Player_level_up::step() { + Player_base::step(); + canvas->level_up--; + if(canvas->level_up < 0) { + canvas->level_up = 0; + ret(); + } +} + +Player_check_line::Player_check_line(Canvas *c): Player_base(c) { +} + +void Player_check_line::step() { + Player_base::step(); + int co = check_nb_line(); + if(co) { + canvas->depth += co; + canvas->complexity++; + check_clean(); + call(new Player_flash_lines(canvas)); + } else { + if(canvas->idle < 2) { // si vivant + //Copy hole positions into moved array so that give_line + // and Net_list::send can use them + int i, j; + for(j=0; j<36; j++) + for(i=0; i<18; i++) + canvas->moved[j][i]=hole_pos[j][i]; + canvas->give_line(); + } else { // si mort, donne rien. + canvas->depth = 0; + canvas->complexity=0; + canvas->send_for_clean=0; + } + ret(); + } +} + +int Player_check_line::check_nb_line() { + int co = 0, i, j; + for(j = 12; j < 32; j++) { + for(i = 4; i < 14; i++) + if(!canvas->occupied[j][i]) + break; + if(i == 14) { + if(co < 20) + canvas->flash[co] = j; + for(i = 4; i < 14; i++) { + if(canvas->occupied[j-1][i] && !(canvas->block[j-1][i]&8)) { + canvas->block[j-1][i] |= 8; + canvas->dirted[j-1][i] = 2; + } + hole_pos[canvas->depth+co][i]=canvas->moved[j][i]; + canvas->block[j][i] = 0; + canvas->occupied[j][i] = false; + canvas->blinded[j][i] = 0; + canvas->bflash[j][i] = 0; + canvas->dirted[j][i] = 2; + if(canvas->occupied[j+1][i] && !(canvas->block[j+1][i]&2)) { + canvas->block[j+1][i] |= 2; + canvas->dirted[j+1][i] = 2; + } + } + co++; + } + } + return co; +} + +void Player_check_line::check_clean() { + int i, j; + for(j = 12; j < 32; j++) + for(i = 4; i < 14; i++) + if(canvas->occupied[j][i]) + return; + canvas->add_text_scroller(ST_CLEANCANVAS); + if(game->net_version()<23) + canvas->stats[CS::SCORE].add(5000); + canvas->send_for_clean=true; +} + +Player_process_key::Player_process_key(Canvas *c): Player_base(c) { + hold_left = hold_right = 0; + last_video_frame = last_overmind_frame = 0; + canvas->idle = 1; + canvas->state = Canvas::PLAYING; + block_rotated=0; + time_held=0; +} + +bool Player_base::rotate_left() { + bool ret=false; + int t, i; + t = (canvas->bloc->rot-1)&3; + if(!canvas->collide(canvas->bloc->bx, canvas->bloc->by, t)) { + ret=true; + canvas->bloc->rot = t; + if(canvas->inter) { + i = (canvas->bloc->bx-9) * 300; + play_sound(sons.drip, -200-ugs_random.rnd(127), i, 10500+ugs_random.rnd(1023)); + } + } + canvas->set_bit(8); + return ret; +} + +bool Player_base::rotate_right(bool twice) { + bool ret=false; + int t, i; + int inc=1; + if(twice) + inc=2; + t = (canvas->bloc->rot+inc)&3; + if(!canvas->collide(canvas->bloc->bx, canvas->bloc->by, t)) { + ret=true; + canvas->bloc->rot = t; + if(canvas->inter) { + i = (canvas->bloc->bx-9) * 300; + play_sound(sons.drip, -200-ugs_random.rnd(127), i, 10500+ugs_random.rnd(1023)); + } + } + if(twice) + canvas->set_bit(32); + else + canvas->set_bit(16); + return ret; +} + +void Player_base::move_down() { + int t; + t = calc_by(canvas->bloc->y+canvas->down_speed); + if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) { + canvas->bloc->y += canvas->down_speed; + canvas->bloc->by = calc_by(canvas->bloc->y); + } else { // sert a 'accoter' le bloc pour qu'il se depose + canvas->bloc->y = ((canvas->bloc->by-12)*18<<4)-1; + } + canvas->set_bit(1); +} + +void Player_base::drop_down() { + while(!canvas->check_collide(canvas->bloc, canvas->bloc->bx, canvas->bloc->by+1, canvas->bloc->rot)) + canvas->bloc->by++; + canvas->bloc->calc_xy(); + canvas->set_bit(64); +} + +bool Player_base::move_left() { + bool ret=false; + if(!canvas->collide(canvas->bloc->bx-1, canvas->bloc->by, canvas->bloc->rot)) { + ret=true; + canvas->bloc->bx--; + } + canvas->set_bit(2); + return ret; +} + +bool Player_base::move_right() { + bool ret=false; + if(!canvas->collide(canvas->bloc->bx+1, canvas->bloc->by, canvas->bloc->rot)) { + ret=true; + canvas->bloc->bx++; + } + canvas->set_bit(4); + return ret; +} + +void Player_process_key::playback_control() { + Byte r = playback->get_byte(); + if(r & 8) + rotate_left(); + if(r & 16) + rotate_right(); + if(r & 1) + move_down(); + if(r & 2) + move_left(); + if(r & 4) + move_right(); +} + +void Player_process_key::keyboard_control() { + if(last_video_frame == video->framecount && overmind.framecount - last_overmind_frame > 3) + return; + if(last_video_frame != video->framecount) { + last_video_frame = video->framecount; + last_overmind_frame = overmind.framecount; + } + + bool bouge_left = false; + bool bouge_right = false; + bool auto_bouge = false; + if(canvas->check_key(2) & RELEASED) { + if(rotate_left()) + block_rotated++; + else { + if(canvas->collide_side_only) { + int inc; + if(canvas->bloc->bx<5) + inc=1; + else + inc=-1; + if(!canvas->collide(canvas->bloc->bx+inc, canvas->bloc->by, canvas->bloc->rot)) { + canvas->bloc->bx+=inc; + if(rotate_left()) { + auto_bouge=true; + block_rotated++; + } + else + canvas->bloc->bx-=inc; + } + } + } + canvas->clear_key(2); + } + if(canvas->check_key(4) & RELEASED) { + if(rotate_right()) + block_rotated++; + else { + if(canvas->collide_side_only) { + int inc; + if(canvas->bloc->bx<5) + inc=1; + else + inc=-1; + if(!canvas->collide(canvas->bloc->bx+inc, canvas->bloc->by, canvas->bloc->rot)) { + canvas->bloc->bx+=inc; + if(rotate_right()) { + auto_bouge=true; + block_rotated++; + } + else + canvas->bloc->bx-=inc; + } + } + } + canvas->clear_key(4); + } + if(canvas->check_key(5) & RELEASED) { + if(rotate_right(true)) + block_rotated+=2; + else { + if(canvas->collide_side_only) { + int inc; + if(canvas->bloc->bx<5) + inc=1; + else + inc=-1; + if(!canvas->collide(canvas->bloc->bx+inc, canvas->bloc->by, canvas->bloc->rot)) { + canvas->bloc->bx+=inc; + if(rotate_right(true)) { + auto_bouge=true; + block_rotated++; + } + else + canvas->bloc->bx-=inc; + } + } + } + canvas->clear_key(5); + } + if(canvas->check_key(3) & PRESSED || canvas->check_key(3) & RELEASED) { + move_down(); + canvas->unrelease_key(3); + } + if(canvas->check_key(6) & RELEASED) { + drop_down(); + canvas->clear_key(6); + } + if(canvas->check_key(0) & PRESSED || canvas->check_key(0) & RELEASED) { + if(!bouge_left) { + bouge_left = true; + canvas->unrelease_key(0); + } + } + if(canvas->check_key(1) & PRESSED || canvas->check_key(1) & RELEASED) { + if(!bouge_right) { + bouge_right = true; + canvas->unrelease_key(1); + } + } + if(bouge_left && !bouge_right && !auto_bouge) { + if(hold_left < 2) { + if(hold_left == 0) + hold_left = canvas->h_repeat_delay+10; // delai de repetition initiale + else + hold_left = canvas->h_repeat_delay; + if(!move_left()) + hold_left=1; + } else + hold_left--; + } else + hold_left = 0; + if(bouge_right && !bouge_left && !auto_bouge) { + if(hold_right < 2) { + if(hold_right == 0) + hold_right = canvas->h_repeat_delay+10; + else + hold_right = canvas->h_repeat_delay; + if(!move_right()) + hold_right=1; + } else + hold_right--; + } else + hold_right = 0; +} + +void Player_process_key::init() { + Player_base::init(); +// canvas->clear_key(2); +// canvas->clear_key(4); +// canvas->clear_key(5); + canvas->clear_key(6); + if(!canvas->continuous) + canvas->clear_key(3); + canvas->start_moves(); +} + +void Player_process_key::step() { + Player_base::step(); + if(check_gone()) + return; + + if(canvas->should_remove_bonus) + remove_bonus(); + + int t, i; + if(game->survivor) + if(check_first_frag()) + return; + if(game->paused) + return; + + time_held++; + canvas->start_byte(); + + if(playback) + playback_control(); + else { + if(canvas->inter) // si canvas invisible, ecoute pas les touches! + keyboard_control(); + } + + canvas->write_byte(); + + t = ((canvas->bloc->bx-4)<<4)*18; // calcul la position ou le bloc devrait etre + int nx = canvas->bloc->x; // nouvelle position 'x' a obtenir + if(nx < t) { + nx += canvas->side_speed; + if(nx > t) // si depasse la position + nx = t; + } + if(nx > t) { + nx -= canvas->side_speed; + if(nx < t) // si depasse la position + nx = t; + } + t = calc_by(canvas->bloc->y-(17<<4)-15); + if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) + canvas->bloc->x = nx; // accepte le deplacement 'smooth' seulement s'il ne passe pas par-dessus un bloc + t = calc_by(canvas->bloc->y+canvas->speed); + if(canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) { + canvas->send_p_moves(); + Packet_clientstampblock p; + p.rotate=canvas->bloc->rot; + p.x=canvas->bloc->bx; + p.y=canvas->bloc->by; + p.player=canvas->num_player; + if(game->net_version()<23) { + i = max(overmind.framecount - canvas->frame_start - 50, 0); + p.score=max(0, 100 - i) >> 1; + } + else + p.score=0; + p.date = canvas->watch_date++; + if(block_rotated>255) + block_rotated=255; + p.block_rotated=block_rotated; + if(time_held>65535) + time_held=65535; + p.time_held=time_held; + net->sendtcp(&p); + exec(new Player_stamp(canvas, &p)); + return; + } + else { + canvas->bloc->y+=canvas->speed; + canvas->bloc->by = calc_by(canvas->bloc->y); + if(canvas->shadow) + canvas->calc_shadow(); + } +} + +bool Player_process_key::check_first_frag() { + if(game->net_list.syncpoint==Canvas::PLAYING) + return false; + if(game->net_list.syncpoint==Canvas::LAST) + return false; + //If syncing toward WAITFORWINNER or WAITFORRESTART, + // go to first_frag state + canvas->send_p_moves(); + Packet_clientfirst_frag pf; + pf.player = canvas->num_player; + net->sendtcp(&pf); + exec(new Player_first_frag(canvas)); + return true; +} + +void Player_get_next::step() { + Player_base::step(); + while(!canvas->bloc) + shift_next(); + + canvas->set_next(); + canvas->frame_start = overmind.framecount; + ret(); +} + +void Player_get_next::shift_next() { + canvas->bloc = canvas->next3; + canvas->next3 = canvas->next2; + canvas->next2 = canvas->next; + Byte the_next; + if(game->net_version()>=23) + the_next=canvas->rnd.rnd()%7; + else + the_next=canvas->rnd.crap_rnd()%7; + canvas->next = new Bloc(the_next, -1, 7, 10); +} + +Player_normal::Player_normal(Canvas *c): Player_base(c) { + canvas->idle = 1; + canvas->state = Canvas::PLAYING; +} + +void Player_normal::step() { + Player_base::step(); + + if(!canvas->bloc) + call(new Player_get_next(canvas)); + else { + //If this is a local player, check death condition + if(canvas->islocal()) { + if(canvas->collide(canvas->bloc->bx, canvas->bloc->by, canvas->bloc->rot)) { + bool tg=false; + if(game->single) + tg=true; + call(new Player_dead(canvas)); + } + else { + call(new Player_process_key(canvas)); + } + } + else { + //Not local, wait for next block or death + call(new Player_wait_block(canvas)); + } + } +} + +Player_dead::Player_dead(Canvas *c, bool tg): Player_base(c), then_gone(tg) { + canvas->dying=true; + for(j=0; j<36; j++) + for(i=0; i<18; i++) { + if(canvas->blinded[j][i]) { + canvas->bflash[j][i] = 24; + canvas->dirted[j][i] = 2; + } + canvas->blinded[j][i] = 0; + } + if(canvas->islocal()) { + //If local, tell server that we died + Packet_clientdead p; + p.player=canvas->num_player; + p.then_gone=then_gone; + net->sendtcp(&p); + } + Canvas* fragger = NULL; + if(canvas->last_attacker != 255) + fragger = game->net_list.get(canvas->last_attacker); + if(fragger) + msgbox("Player_dead::Player_dead: %s a ete fragger par %s. attacks[]=", canvas->name, fragger->name); + else + msgbox("Player_dead::Player_dead: %s est mort sans frag. attacks[]=", canvas->name); + for(int i2=0; i2attacks[i2]); + msgbox("\n"); + if(fragger) { + fragger->stats[CS::FRAG].add(1); + sprintf(st, ST_BOBFRAGBOB, fragger->name, c->name); + message(fragger->color, st); + + couleur = fragger->color<<4; // couleur du 'fragger' + sprintf(st, ST_YOUFRAGBOB, c->name); + fragger->add_text_scroller(st); + } else { + sprintf(st, ST_BOBDIED, c->name); + message(-1, st); + couleur = 8<<4; // gris pour mort naturelle + } + canvas->stats[CS::DEATH].add(1); + char *death_type="normal"; + //Suicide: less than 9 unit blocks in the 2 lines right + // below the top + int num_units=0; + for(i=14; i<16; i++) + for(j=4; j<14; j++) + if(canvas->occupied[i][j]) + num_units++; + bool suicide=false; + if(num_units<9) + suicide=true; + if(then_gone) { + death_type="left"; + suicide=false; + } + //Overkill and maximum overkill + for(i=0; i<12; i++) { + num_units=0; + for(j=4; j<14; j++) + if(canvas->occupied[i][j]) + num_units++; + if(num_units>=8) + break; + } + if(i<10 && i>=7) { + death_type="overkill"; + if(fragger) + fragger->stats[CS::OVERKILLER].add(1); + canvas->stats[CS::OVERKILLEE].add(1); + suicide=false; + } + if(i<7) { + death_type="maximum_overkill"; + if(fragger) + fragger->stats[CS::MAXOVERKILLER].add(1); + canvas->stats[CS::MAXOVERKILLEE].add(1); + suicide=false; + } + if(suicide) { + death_type="suicide"; + canvas->stats[CS::SUICIDES].add(1); + } + log_step("player_dead\t%u\t%u\t%s", canvas->id(), fragger? fragger->id():0, death_type); + i = 11; + j = 4; + Player_dead::c = 0; + canvas->delete_bloc(); + canvas->set_next(); +} + +void Player_dead::step() { + Player_base::step(); + + if(c++&1) + return; + if(j < 9) { + if(canvas->occupied[i][j]) { + canvas->block[i][j] = (canvas->block[i][j] & 15) + couleur; + canvas->dirted[i][j] = 2; + } + if(canvas->occupied[42-i][j]) { + canvas->block[42-i][j] = (canvas->block[42-i][j] & 15) + couleur; + canvas->dirted[42-i][j] = 2; + } + if(canvas->occupied[i][17-j]) { + canvas->block[i][17-j] = (canvas->block[i][17-j] & 15) + couleur; + canvas->dirted[i][17-j] = 2; + } + if(canvas->occupied[42-i][17-j]) { + canvas->block[42-i][17-j] = (canvas->block[42-i][17-j] & 15) + couleur; + canvas->dirted[42-i][17-j] = 2; + } + int vo = -600; + if(!canvas->islocal()) + vo -= 1000; + + i++; + if(i == 22) { + j++; + i = 11; + if(canvas->inter) + play_sound(sons.flash, vo, -1, 22500+ugs_random.rnd(1023)); + } + } + else { + canvas->dying=false; + if(then_gone) + exec(new Player_gone(canvas, true)); + else + if(canvas->islocal()) + exec(new Player_dead_wait(canvas)); + else + exec(new Player_wait_respawn(canvas)); + } +} + +Player_dead_wait::Player_dead_wait(Canvas *c, bool ab): Player_base(c), add_bonus(ab) { + if(canvas->inter) + canvas->clear_key_all(); + canvas->idle = 2; + canvas->state = Canvas::WAITFORWINNER; +} + +void Player_dead_wait::init() { + Player_base::init(); + if(game->single || game->delay_start) + return; + if(game->survivor) + canvas->set_message(ST_WAITINGFORROUND1, ST_WAITINGFORROUND2); + else + canvas->set_message(ST_WAITINGFORKEY1, ST_WAITINGFORKEY2); +} + +void Player_dead_wait::step() { + Player_base::step(); + if(check_gone()) + return; + + if(canvas->should_remove_bonus) + remove_bonus(); + + bool restart=false; + + if(playback && playback->old_mode) { + if(playback->get_byte()) + restart = true; + } + else { + if(canvas->inter) {// si canvas invisible, ecoute pas les touches! + for(int i=0; i<7; i++) + if(canvas->check_key(i)) + restart = true; + } + } + + if(game->survivor) { + restart = check_first_frag(); + } + else + if(game->hot_potato && canvas->color==game->potato_team) { + //Sorry mister, your buddies are still juggling the hot + // potato, you can't go help them yet! :) + restart=false; + } + //Never restart in single-player games + if(game->single) + restart=false; + if(restart) { + Packet_clientrespawn p; + p.player=canvas->num_player; + net->sendtcp(&p); + canvas->restart(); + ret(); + log_step("player_respawn\t%u", canvas->id()); + } + else { + if(canvas->bonus && !canvas->bon[0].blind_time && add_bonus) + call(new Player_add_bonus(canvas)); + } +} + +bool Player_dead_wait::check_first_frag() { + switch(canvas->state) { + case Canvas::WAITFORWINNER: + if(game->net_list.syncpoint==Canvas::WAITFORRESTART) { + canvas->state = Canvas::WAITFORRESTART; + Packet_clientstate p; + p.player = canvas->num_player; + p.state = canvas->state; + net->sendtcp(&p); + } + break; + case Canvas::WAITFORRESTART: + if(game->net_list.syncpoint==Canvas::PLAYING) + return true; + case Canvas::PLAYING: + case Canvas::LAST: + break; + } + return false; +} + +Player_first_frag::Player_first_frag(Canvas *c): Player_base(c) { + canvas->dying=true; + for(j=0; j<36; j++) + for(i=0; i<18; i++) { + if(canvas->blinded[j][i]) { + canvas->bflash[j][i] = 24; + canvas->dirted[j][i] = 2; + } + canvas->blinded[j][i] = 0; + } + couleur = 8<<4; + i = 11; + j = 4; + this->c = 0; + canvas->delete_bloc(); + canvas->set_next(); + canvas->stats[CS::ROUND_WINS].add(1); + log_step("player_survived\t%u", canvas->id()); +} + +void Player_first_frag::step() { + Player_base::step(); + + if(c++&1) + return; + if(j < 9) { + canvas->block[i][j] = 15 + couleur; + canvas->occupied[i][j] = true; + canvas->dirted[i][j] = 2; + canvas->block[42-i][j] = 15 + couleur; + canvas->occupied[42-i][j] = true; + canvas->dirted[42-i][j] = 2; + canvas->block[i][17-j] = 15 + couleur; + canvas->occupied[i][17-j] = true; + canvas->dirted[i][17-j] = 2; + canvas->block[42-i][17-j] = 15 + couleur; + canvas->occupied[42-i][17-j] = true; + canvas->dirted[42-i][17-j] = 2; + int vo = -600; + if(!canvas->islocal()) + vo -= 1000; + + i++; + if(i == 22) { + j++; + i = 11; + if(canvas->inter) + play_sound(sons.flash, vo, 1000, 32500+ugs_random.rnd(1023)); + } + } else { + canvas->dying=false; + Player_base *pb; + if(canvas->islocal()) { + pb=new Player_dead_wait(canvas, false); + } + else { + pb=new Player_wait_respawn(canvas, false); + } + exec(pb); + } +} + +Player_gone::Player_gone(Canvas *c, bool chat_msg): Player_base(c) { + canvas->local_player = false; // enleve le statut de 'local' a un joueur gone + canvas->idle = 3; + canvas->state = Canvas::LAST; + + canvas->delete_bloc(); + canvas->set_next(); + if(chat_msg) { + sprintf(st, ST_BOBHASGONE, canvas->name); + message(-1, st); + } + log_step("player_gone\t%u", canvas->id()); +} + +void Player_gone::step() { + Player_base::step(); + if(canvas->islocal()) { + if(canvas->should_remove_bonus) + remove_bonus(); + } + else { + check_bonus(); + } + canvas->gone_time++; + + { + Packet_dead *p=(Packet_dead *) game->peekpacket(P_DEAD); + if(p && p->player==canvas->num_player && p->then_gone) + game->removepacket(); //Remove superfluous gone packet sent by server if client disconnects too fast + } + + { + Packet_gone *p=(Packet_gone *) game->peekpacket(P_GONE); + if(p && p->player==canvas->num_player) + game->removepacket(); //Remove superfluous gone packet sent by server if client disconnects too fast + } + + Packet_rejoin *p=(Packet_rejoin *) game->peekpacket(P_REJOIN); + if(p && p->player==canvas->num_player) { + canvas->h_repeat = p->h_repeat; + canvas->v_repeat = p->v_repeat; + canvas->smooth = p->smooth? true:false; + canvas->shadow = p->shadow? true:false; + canvas->handicap = p->handicap; + sprintf(st, ST_BOBREJOIN, canvas->long_name(true, false)); + message(-1, st); + msgbox("Player_gone::step: player %i is no longer a goner! Rejoining.\n", p->player); + game->removepacket(); + + game->net_list.rejoin_player(canvas); + canvas->delete_bloc(); + canvas->reinit(); + canvas->restart(); + game->net_list.notify_all(); + if(game->net_version()>=23 && game->survivor) { + if(canvas->islocal()) + exec(new Player_dead_wait(canvas)); + else + exec(new Player_wait_respawn(canvas)); + } + ret(); + } +} + +Player_wait_block::Player_wait_block(Canvas *c): Player_base(c) { + canvas->idle = 1; + canvas->state = Canvas::PLAYING; + move_index=0; +} + +Player_wait_block::~Player_wait_block() { + int total_left=0; + if(move_indexda_moves.size()) + total_left+=canvas->da_moves.size()-move_index; + if(total_left) + msgbox("Player_wait_block::deletemoves: moves not exhausted, %i left\n", total_left); + canvas->da_moves.resize(0); +} + +void Player_wait_block::init() { + Player_base::init(); +} + +void Player_wait_block::step() { + Player_base::step(); + bool wait_more=false; + if(playback && !playback->old_mode) { + int remain=canvas->da_moves.size()-move_index; + if(remain && remain<=50) + wait_more=true; + } + + if(!wait_more) + if(check_gone()) + return; + + check_bonus(); + + if(!wait_more) + if(game->survivor) + if(check_first_frag()) + return; + + if(!(playback && playback->old_mode)) { + { + Packet_moves *p=(Packet_moves *) game->peekpacket(P_MOVES); + if(p && p->player==canvas->num_player) { + if(canvas->da_moves.size()+p->size<4000) + canvas->da_moves.append(p->moves, p->size); + game->removepacket(); + } + } + if(move_indexda_moves.size() && !game->paused) { + Byte r = canvas->da_moves[move_index++]; + if(r & 8) + rotate_left(); + if(r & 16) + rotate_right(); + if(r & 32) + rotate_right(true); + if(r & 1) + move_down(); + if(r & 64) + drop_down(); + if(r & 2) + move_left(); + if(r & 4) + move_right(); + + int t; + t = ((canvas->bloc->bx-4)<<4)*18; // calcul la position ou le bloc devrait etre + int nx = canvas->bloc->x; // nouvelle position 'x' a obtenir + if(nx < t) { + nx += canvas->side_speed; + if(nx > t) // si depasse la position + nx = t; + } + if(nx > t) { + nx -= canvas->side_speed; + if(nx < t) // si depasse la position + nx = t; + } + t = calc_by(canvas->bloc->y-(17<<4)-15); + if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) + canvas->bloc->x = nx; // accepte le deplacement 'smooth' seulement s'il ne passe pas par-dessus un bloc + t = calc_by(canvas->bloc->y+canvas->speed); + if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) { + canvas->bloc->y+=canvas->speed; + canvas->bloc->by = calc_by(canvas->bloc->y); + } + if(canvas->shadow) + canvas->calc_shadow(); + } + } + + if(wait_more) + return; + + //Check whether this player placed a block + Packet_stampblock *p=(Packet_stampblock *) game->peekpacket(P_STAMPBLOCK); + if(p && p->player==canvas->num_player) { + exec(new Player_stamp(canvas, p)); + game->removepacket(); + return; + } + + //Check whether this player died + Packet_dead *pd=(Packet_dead *) game->peekpacket(P_DEAD); + if(pd && pd->player==canvas->num_player) { + exec(new Player_dead(canvas, pd->then_gone)); + game->removepacket(); + return; + } +} + +bool Player_wait_block::check_first_frag() { + Packet_first_frag *p=(Packet_first_frag *) game->peekpacket(P_FIRST_FRAG); + if(p && p->player==canvas->num_player) { + msgbox("Player_wait_block::check_first_frag: recu P_FIRST_FRAG: %s gagnant!\n", canvas->name); + exec(new Player_first_frag(canvas)); + game->removepacket(); + return true; + } + return false; +} + +Player_stamp::Player_stamp(Canvas *c, Packet_stampblock *p): Player_base(c) { + /* Flash blinded blocks when stamping + int i, j; + for(j=0; j<36; j++) + for(i=0; i<18; i++) + if(canvas->blinded[j][i]) { + canvas->bflash[j][i] = 8; + } + */ + int i, j; + //Reset Canvas::moved + for(j=0; j<36; j++) + for(i=0; i<18; i++) + canvas->moved[j][i]=false; + canvas->idle = 0; + canvas->potato_team_on_last_stamp=game->potato_team; + canvas->bloc->rot=p->rotate; + canvas->bloc->bx=p->x; + canvas->bloc->by=p->y; + if(canvas->collide(p->x, p->y, p->rotate) || !canvas->collide(p->x, p->y+1, p->rotate)) { + char st[256]; + sprintf(st, ST_BOBINVALIDBLOCK, canvas->name); + if(game->server) { + message(-1, st, true, false, true); + game->net_list.server_drop_player(canvas->num_player, DROP_INVALID_BLOCK); + Net_connection *nc=canvas->remote_adr; + send_msg(nc, "You have been dropped because you placed an invalid block. This may be due to a bug in the game or a network failure."); + send_msg(nc, "Quitting and rejoining the game should fix the problem. If not, please contact support@ludusdesign.com."); + } + msgbox("%s\n", st); + } + canvas->stats[CS::COMPTE0+canvas->bloc->quel].add(1); + canvas->stats[CS::COMPTETOT].add(1); + canvas->stats[CS::ROTATED0+canvas->bloc->quel].add(p->block_rotated); + canvas->stats[CS::ROTATEDTOT].add(p->block_rotated); + canvas->stats[CS::SCORE].add(p->score); + if(game->net_version()>=23) + canvas->stats[CS::PLAYING_TIME].add(p->time_held); + log_step("player_stampblock\t%u\t%i\t%i\t%i\t%i", canvas->id(), canvas->bloc->quel, p->block_rotated, p->time_held, p->score); + canvas->watch_date = p->date; + stamp_bloc(); + if(canvas->bonus && !canvas->bon[0].blind_time) + addbonus=new Player_add_bonus(canvas); + else + addbonus=NULL; + + for(i=0; iattacks[i] > 0) { + canvas->attacks[i]--; + if(canvas->attacks[i] == 0 && canvas->last_attacker == i) // si c'etait lui le last_attacker, + if(!game->survivor) + canvas->last_attacker = 255; // on l'oublie. + } + } +} + +void Player_stamp::init() { + if(addbonus) { + call(new Player_check_line(canvas)); + call(addbonus); + } + call(new Player_check_line(canvas)); + ret(); +} + +void Player_stamp::stamp_bloc() { + int i,j; + Byte t; + Byte blindness=0; + if(canvas->bonus && canvas->bon[0].blind_time) { + blindness=canvas->bon[0].blind_time; + int y; + for(y = 1; y < canvas->bonus; y++) + canvas->bon[y-1] = canvas->bon[y]; + canvas->bonus--; + } + for(j = 0; j < 4; j++) + for(i = 0; i < 4; i++) { + t = canvas->bloc->bloc[canvas->bloc->quel][canvas->bloc->rot][j][i]; + if(t) { + canvas->block[canvas->bloc->by+j][canvas->bloc->bx+i] = t + (canvas->bloc->quel<<4); + canvas->occupied[canvas->bloc->by+j][canvas->bloc->bx+i] = true; + canvas->blinded[canvas->bloc->by+j][canvas->bloc->bx+i] = blindness; + canvas->bflash[canvas->bloc->by+j][canvas->bloc->bx+i] = blindness? 16:0; + canvas->dirted[canvas->bloc->by+j][canvas->bloc->bx+i] = 2; + canvas->last_x = i; + canvas->moved[canvas->bloc->by+j][canvas->bloc->bx+i] = true; + } + } + canvas->last_x += canvas->bloc->bx; + canvas->snapshot[0]=0; + for(j = 12; j < 32; j++) + for(i = 4; i < 14; i++) { + if(canvas->occupied[j][i]) { + char bl[3]; + Byte color; + if(canvas->moved[j][i]) + color=8; + else + color=canvas->block[j][i]>>4; + Byte side=canvas->block[j][i]&15; + sprintf(bl, "%c%c", '0'+color, 'a'+side); + strcat(canvas->snapshot, bl); + } + else + strcat(canvas->snapshot, "e0"); + } + i = (canvas->bloc->bx-9) * 300; + Sample *dep; + switch(ugs_random.rnd()%3) { + case 0: dep = sons.depose; + break; + case 1: dep = sons.depose2; + break; + default: dep = sons.depose3; + break; + } + if(canvas->inter) { + int vo=-200-ugs_random.rnd(255); + if(!canvas->islocal()) + vo -= 1000; + play_sound(dep, vo, i, 10500+ugs_random.rnd(1023)); + } + + delete canvas->bloc; + canvas->bloc = NULL; + if(canvas->bloc_shadow) { + delete canvas->bloc_shadow; + canvas->bloc_shadow = NULL; + } +} + +Player_wait_respawn::Player_wait_respawn(Canvas *c, bool ab): Player_base(c), add_bonus(ab) { + canvas->idle = 2; + canvas->state = Canvas::WAITFORWINNER; +} + +void Player_wait_respawn::step() { + Player_base::step(); + if(check_gone()) + return; + + check_bonus(); + + check_state(); + + Packet_respawn *p=(Packet_respawn *) game->peekpacket(P_RESPAWN); + if(p && p->player==canvas->num_player) { + canvas->restart(); + ret(); + game->removepacket(); + log_step("player_respawn\t%u", canvas->id()); + } else { + if(canvas->bonus && !canvas->bon[0].blind_time && add_bonus) + call(new Player_add_bonus(canvas)); + } +} + +Player_init::Player_init(Canvas *canvas): Player_base(canvas) { + canvas->restart(); + if(game->net_version()>=23 && game->survivor) + canvas->idle = 2; + else + canvas->idle = 1; + canvas->state = Canvas::PLAYING; + if(canvas->wait_download) + net->addwatch(P_DOWNLOAD, this); +} + +void Player_init::step() { + Player_base::step(); + //Don't start until we got our download packet + if(canvas->wait_download) + return; + Byte idle=canvas->idle; //Cause Player_normal::Player_normal sets it + exec(new Player_normal(canvas)); + if(idle == 2) { + if(canvas->islocal()) + call(new Player_dead_wait(canvas)); + else + call(new Player_wait_respawn(canvas)); + } + if(idle == 3) { + call(new Player_gone(canvas, false)); + canvas->remote_adr = NULL; // empeche de watcher un joueur qui est 'gone' lorsqu'on join + } +} + +void Player_init::net_call(Packet *p2) { + Packet_download *p=(Packet_download *) p2; + { + Canvas *c = game->net_list.get(p->player); + if(!c || c!=canvas) { + msgbox("Player_init::net_call: got a P_DOWNLOAD but canvas does not exist or not even mine, something screwy is going on\n"); + delete p2; + return; + } + } + for(int y=0; y<32; y++) + for(int x=0; x<10; x++) { + canvas->block[y][x+4] = p->can[y][x]; + canvas->occupied[y][x+4] = p->occ[y][x]; + canvas->blinded[y][x+4] = p->blinded[y][x]; + } + canvas->rnd.set_seed(p->seed); + if(p->bloc != 255) { + canvas->bloc = new Bloc(p->bloc, -1, 7, 10); + canvas->next = new Bloc(p->next, -1, 7, 10); + canvas->next2 = new Bloc(p->next2, -1, 7, 10); + canvas->next3 = new Bloc(p->next3, -1, 7, 10); + } + canvas->bonus = p->bonus; + int i; + for(i=0; i<20; i++) { + canvas->bon[i].x = p->bon[i].x; + canvas->bon[i].color = p->bon[i].color; + canvas->bon[i].blind_time = p->bon[i].blind_time; + canvas->bon[i].hole_pos = p->bon[i].hole_pos; + canvas->bon[i].final = p->bon[i].final; + } + for(i=0; iattacks[i] = p->attacks[i]; + canvas->last_attacker = p->last_attacker; + canvas->idle = p->idle; + canvas->state = (Canvas::State) p->state; + msgbox("Player_init::net_call: name=%s, idle=%i, bonus=%i, bloc=%i\n", canvas->name, canvas->idle, canvas->bonus, p->bloc); + delete p2; + canvas->wait_download=false; + net->removewatch(P_DOWNLOAD, this); +} + +void init_directory() { + strcpy(quadradir, exe_directory); +#ifdef UGS_LINUX + struct passwd *pw = NULL; + + pw = getpwuid(getuid()); + if(pw) { + strcpy(quadradir, pw->pw_dir); + strcat(quadradir, "/.quadra"); + mkdir(quadradir, 0777); + chown(quadradir, pw->pw_uid, pw->pw_gid); + } +#endif +} + +void init_stuff(bool need_sound=true, bool need_video=true) { + int i; + + video = Video::New(640, 480, 8, "Quadra", !need_video); + + if(!video) + (void)new Error("Could not initialize video subsystem"); + + fonts.init(); + if(!video->xwindow) { + Res_doze res("CURSOR.RAW"); + Raw raw(res); + Bitmap bitmap(raw); + cur = new Sprite(bitmap, 0, 0); + } + else + cur=NULL; + //If we init a dumb video, we need a dumb input too + input = Input::New(!need_video); + if(need_sound && need_video) { // don't need sound if no video + sound = new Sound(); + if(!sound->active) { + delete sound; + sound = NULL; + } + } + else + sound = NULL; + + if(need_video) + music = Music::alloc(); + else + music = NULL; + + for(i=0; i<256; i++) + noir.setcolor(i, 40,40,40); + + chat_text = new Chat_text(fonts.normal, 212); + net_starter = new Net_starter(); + { + Res_doze res("cuckoo.wav"); + sons.pause = new Sample(res, 2); + } + { + Res_doze res("hooter03.wav"); + sons.start = new Sample(res,2); + } + { + Res_doze res("Whizz1.wav"); + sons.bonus1 = new Sample(res, 2); + } + { + Res_doze res("glissup.wav"); + sons.levelup = new Sample(res, 2); + } + { + Res_doze res("Clang3.wav"); + sons.depose4 = new Sample(res, 2); // quand le canvas 'coule' + } + sons.flash = NULL; + sons.depose3 = NULL; + sons.depose2 = NULL; + sons.depose = NULL; + sons.drip = NULL; + { + Res_doze res("Glass01.wav"); + sons.glass = new Sample(res, 2); + } + { + Res_doze res("Tapdrip.wav"); + sons.enter = new Sample(res, 2); + } + { + Res_doze res("W_BAYO_0.wav"); + sons.fadein = new Sample(res, 2); + } + { + Res_doze res("fadeout.wav"); + sons.fadeout = new Sample(res, 2); + } + { + Res_doze res("click_1.wav"); + sons.point = new Sample(res, 2); + } + { + Res_doze res("Blip1.wav"); + sons.click = new Sample(res, 2); + } + { + Res_doze res("handbell.wav"); + sons.msg = new Sample(res, 2); + } + { + Res_doze res("potato_get.wav"); + sons.potato_get = new Sample(res, 2); + } + { + Res_doze res("zingle.wav"); + sons.potato_rid = new Sample(res, 2); + } + cursor = Cursor::New(cur); + cursor->set_speed(config.info.mouse_speed); + for(i=0; i<8; i++) + fteam[i] = new Font(*fonts.normal); + + config.check_register(); +} + +void deinit_stuff() { + for(int i=0; i<8; i++) + if(fteam[i]) { + delete fteam[i]; + fteam[i] = NULL; + } + if(sons.click) { + delete sons.click; + sons.click = NULL; + } + if(sons.point) { + delete sons.point; + sons.point = NULL; + } + if(sons.fadeout) { + delete sons.fadeout; + sons.fadeout = NULL; + } + if(sons.fadein) { + delete sons.fadein; + sons.fadein = NULL; + } + if(sons.drip) { + delete sons.drip; + sons.drip = NULL; + } + if(sons.glass) { + delete sons.glass; + sons.glass = NULL; + } + if(sons.depose) { + delete sons.depose; + sons.depose = NULL; + } + if(sons.depose2) { + delete sons.depose2; + sons.depose2 = NULL; + } + if(sons.depose3) { + delete sons.depose3; + sons.depose3 = NULL; + } + if(sons.depose4) { + delete sons.depose4; + sons.depose4 = NULL; + } + if(sons.flash) { + delete sons.flash; + sons.flash = NULL; + } + if(sons.enter) { + delete sons.enter; + sons.enter = NULL; + } + if(sons.levelup) { + delete sons.levelup; + sons.levelup = NULL; + } + if(sons.bonus1) { + delete sons.bonus1; + sons.bonus1 = NULL; + } + if(sons.start) { + delete sons.start; + sons.start = NULL; + } + if(sons.pause) { + delete sons.pause; + sons.pause = NULL; + } + if(sons.msg) { + delete sons.msg; + sons.msg = NULL; + } + if(sons.potato_get) { + delete sons.potato_get; + sons.potato_get = NULL; + } + if(sons.potato_rid) { + delete sons.potato_rid; + sons.potato_rid = NULL; + } + if(net_starter) { + delete net_starter; + net_starter=NULL; + } + if(chat_text) { + delete chat_text; + chat_text=NULL; + } + config.write(); + Highscores::free(); + if(cursor) { + delete cursor; + cursor = NULL; + } + if(cur) { + delete cur; + cur=NULL; + } + fonts.deinit(); +} + +char *command_get_param(const char *t, char *def=NULL) { + char *temp = command.get_param(); + if(!temp) + temp=def; + if(!temp) { + sprintf(st, "Command line parameter not found for '%s'", t); + (void) new Error(st); + } + return temp; +} + +Attack read_attack_param(const char *s) { + Attack ret; + char temp[1024]; + strncpy(temp, s, sizeof(temp)); + temp[sizeof(temp)-1]=0; + char *param=strchr(temp, ' '); + if(param) { + *param=0; + param++; + } + if(!strcmp(temp, "lines")) + ret.type=ATTACK_LINES; + if(!strcmp(temp, "none")) + ret.type=ATTACK_NONE; + if(!strcmp(temp, "blind") || !strcmp(temp, "fullblind")) { + if(param) { + int p=atoi(param); + p=min(max(1, p), 255); + ret.param=p; + } + } + if(!strcmp(temp, "blind")) { + ret.type=ATTACK_BLIND; + if(!param) + ret.param=30; + } + if(!strcmp(temp, "fullblind")) { + ret.type=ATTACK_FULLBLIND; + if(!param) + ret.param=12; + } + return ret; +} + +void display_command_line_help() { + char st[4096]; + const char *res; + switch(config.info.language) { + default: + case 0: + res="help_en.txt"; break; + case 1: + res="help_fr.txt"; break; + } + Res_doze cmdline(res); + Dword size = min(sizeof(st)-1, cmdline.size()); + strncpy(st, (char *)cmdline.buf(), size); + st[size] = 0; + if(video) + delete video; + user_output(ST_CMDLINE, st); +} + +void read_script(const char *fn, bool second=false) { + char st[32768]; + Res_dos script(fn); + if(script.exist) { + //-2 because Stringtable is strange + Dword size = min(sizeof(st)-2, script.size()); + strncpy(st, (char *)script.buf(), size); + st[size] = 0; + Stringtable str((Byte *)st, size); + int i; + for(i=0; inet_list.got_admin_line(str.get(i), NULL); + } + else { + if(str.get(i)[0]=='-') + command.add(str.get(i)); + } + } + } + else + msgbox("Can't find script %s, ignoring.\n", fn); +} + +#ifdef UGS_DIRECTX +#ifdef _DEBUG +#include +#endif +#endif + +void start_game() { + if(command.token("113")) + Config::net_version = 20; + if(command.token("debug")) { + _debug = true; + msgbox("Debug mode enabled\n"); + } +#ifdef UGS_DIRECTX +#ifdef _DEBUG + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + + int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + tmpDbgFlag |= _CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF*/ | _CRTDBG_LEAK_CHECK_DF; + _CrtSetDbgFlag(tmpDbgFlag); +#endif +#endif + + bool no_video = false; + bool no_sound = false; + bool demo_play = false; + bool demo_verif = false; + char buf[512]; + char fn[1024]; + + init_directory(); + + char *dir=quadradir; +#ifdef UGS_LINUX + dir = getenv("QUADRADIR"); + if(!dir) + dir="/usr/lib/games"; +#endif + resmanager=new Resmanager(); + sprintf(fn, "%s/quadra.res", dir); + resmanager->loadresfile(fn); + sprintf(fn, "%s/quadra%i%i%i.res", dir, Config::major, Config::minor, Config::patchlevel); + resmanager->loadresfile(fn); + config.read(); + //Read script and add to command line options if applicable + if(command.token("exec")) { + char *temp=command_get_param("exec "); + read_script(temp); + } + bool dedicated=command.token("dedicated"); + if(command.token("english") || dedicated) + config.info.language=0; + if(command.token("french")) + config.info.language=1; + char *language; + int i; + switch(config.info.language) { + default: + case 0: + language="anglais.txt"; break; + case 1: + language="francais.txt"; break; + } + for(i=0; i"); + strcpy(buf, temp); + demo_play = true; + demo_verif = true; + no_sound = true; + } + if(command.token("play")) { + char *temp = command_get_param("play "); + strcpy(buf, temp); + demo_play = true; + demo_verif = false; + } + if(!demo_play && dedicated) + no_video=true; + if(command.token("novideo")) { + no_video=true; + no_sound=true; + } + if(command.token("nosound")) { + no_sound=true; + } + init_stuff(!no_sound, !no_video); //No sound when checking demos + Dword last=0; + Dword acc=0; + Executor *menu = new Executor(); + //Add Menu_intro so we get back there after -connect, -server or -play + // unless -thenquit option si specified + if(!command.token("thenquit")) + if(Config::xtreme) + menu->add(new Menu_guy()); + else + menu->add(new Menu_intro()); + + if(!demo_play) { + if(command.token("server") || dedicated) { + if(!net->active) + (void) new Error("Network failed to initialize or not present\nCan't start server.\n"); + buf[0] = 0; + if(command.token("port")) { + char *temp = command_get_param("port "); + int port = atoi(temp); + if(port<=0 || port>=65535) + (void) new Error("Illegal port number.\n"); + config.info.port_number = port; + } + Game_params p; + if(command.token("ffa")) + p.set_preset(PRESET_FFA); + if(command.token("survivor")) + p.set_preset(PRESET_SURVIVOR); + if(command.token("hotpotato")) + p.set_preset(PRESET_HOT_POTATO); + if(command.token("peace")) + p.set_preset(PRESET_PEACE); + if(command.token("blind")) { + p.set_preset(PRESET_BLIND); + char *temp = command_get_param("blind ", "30"); + Dword time=atoi(temp); + time=min(max(time, 0), 255); + p.normal_attack.param=time; + p.clean_attack.param=time; + } + if(command.token("fullblind")) { + p.set_preset(PRESET_FULLBLIND); + char *temp = command_get_param("fullblind ", "12"); + Dword time=atoi(temp); + time=min(max(time, 0), 255); + p.normal_attack.param=time; + p.clean_attack.param=time; + } + if(command.token("attack")) { + char *temp=command_get_param("attack [strength]"); + p.normal_attack=read_attack_param(temp); + } + if(command.token("attackclean")) { + char *temp=command_get_param("attackclean [strength]"); + p.clean_attack=read_attack_param(temp); + } + if(command.token("attack2")) { + char *temp=command_get_param("attack2 [strength]"); + p.potato_normal_attack=read_attack_param(temp); + } + if(command.token("attack2clean")) { + char *temp=command_get_param("attack2clean [strength]"); + p.potato_clean_attack=read_attack_param(temp); + } + if(command.token("nolevelup")) + p.level_up = false; + if(command.token("levelup")) + p.level_up = true; + if(command.token("level")) { + char *temp = command_get_param("level "); + p.level_start = atoi(temp); + p.level_start = min(max(p.level_start, 1), 40); + } + if(command.token("name")) { + char *temp = command_get_param("name "); + strcpy(buf, temp); + p.name=buf; + } + if(command.token("nohandicap")) + p.allow_handicap=false; + if(command.token("endfrag")) + p.game_end = END_FRAG; + if(command.token("endfrags")) + p.game_end = END_FRAG; + if(command.token("endtime")) + p.game_end = END_TIME; + if(command.token("endpoints")) + p.game_end = END_POINTS; + if(command.token("endscore")) + p.game_end = END_POINTS; + if(p.game_end != END_NEVER) { + char *temp = command_get_param("endfrag/endtime/endpoints "); + p.game_end_value = atoi(temp); + p.game_end_value = min(max(p.game_end_value, 1), p.game_end<=END_TIME? 9999:99999); + } + if(command.token("public")) + p.game_public = true; + menu->add(new Menu_startserver()); + (void)new Game(&p); + if(dedicated && !command.token("once")) + game->auto_restart = true; + if(command.token("nomoves")) + game->wants_moves = false; + //Read script a second time, now that game is created + if(command.token("exec")) { + char *temp=command_get_param("exec "); + read_script(temp, true); + } + if(command.token("admin")) { + char *temp = command_get_param("admin "); + char line[1024]; + sprintf(line, "/setpasswd %s", temp); + game->net_list.got_admin_line(line, NULL); + } + if(command.token("record")) { + char *temp = command_get_param("record ", Clock::absolute_time()); + game->prepare_recording(temp); + } + if(command.token("slog")) { + char *temp = command_get_param("slog ", Clock::absolute_time()); + game->prepare_logging(temp); + } + } + else { + if(command.token("connect")) { + if(!net->active) + (void) new Error("Network failed to initialize or not present\nCan't connect.\n"); + char *temp = command_get_param("connect "); + strcpy(buf, temp); + menu->add(new Menu_startconnect(buf, false)); + if(config.warning) + menu->add(new Menu_setup()); + } + } + } + else { + Res_compress *res = new Res_compress(buf, RES_TRY); + if(res->exist) { + menu->add(new Demo_multi_player(res)); + // le 'delete res' est fait par ~Demo_multi_player + } + else { + msgbox("Unable to open demo '%s'\n", buf); + delete res; + } + } + + overmind.start(menu); + bool reset_time=false; + while(!menu->done) { + last=getmsec(); + if(demo_verif) { + acc=500; + while(acc--) + overmind.step(); + } + else { + #ifdef PAINTDETECTOR2000 + bool sounded=false; + #endif + while(acc>=10) { + if(reset_time) { // remet 'normal' seulement si au moins 1 frame s'est ecoule + time_control = TIME_NORMAL; + reset_time = false; + } + acc-=10; + try { + overmind.step(); + } + catch(exception *e) { + msgbox("Exception caught from overmind.step(): %s\n", e->what()); + } + #ifdef PAINTDETECTOR2000 + if(video->need_paint==2 && !sounded) { + Sfx stmp(sons.msg, 0, 0, 0, 11025); + sounded=true; + } + #endif + reset_time=true; + if(time_control == TIME_FREEZE) + break; + } + } + start_frame(); + if(ecran && !video_is_dumb) { + try { + ecran->draw_zone(); + } + catch(exception *e) { + msgbox("Exception caught from ecran->draw_zone(): %s\n", e->what()); + } + + #ifdef FRAMECOUNTER + static Dword lastvideoframe=0, lastoverframe=0; + if(ecran->font) { + if(overmind.framecount-lastoverframe > 500) { + lastoverframe = overmind.framecount; + lastvideoframe = video->framecount; + } + int up = 999; + if(overmind.framecount-lastoverframe > 0) + up = ((video->framecount-lastvideoframe) * 100) / (overmind.framecount-lastoverframe); + video->vb->rect(0,0,50,20,0); + char st[80]; + sprintf(st, "%i", up); + ecran->font->draw(st, video->vb, 0, 0); + } + #endif /* FRAMECOUNTER */ + } + end_frame(); + +#ifdef _DEBUG + if(input->keys[KEY_F8] & PRESSED) // F8 = buckage + for(int j=0; j<8000000; j++) + ; + if(input->keys[KEY_F9] & PRESSED) // F9 = slow motion mode + time_control = TIME_SLOW; + if(input->keys[KEY_F10] & PRESSED) // F10 = turbo mode + time_control = TIME_FAST; +#endif + + switch(time_control) { + case TIME_FREEZE: acc = 10; break; + case TIME_SLOW: acc += 1; break; + case TIME_FAST: acc += 80; break; + default: acc+=getmsec()-last; + } + if(acc > 300 && !video_is_dumb) { + overmind.framecount+=acc-300; + acc = 300; // pour eviter trop de depassement + } + if(acc > 10000) { + msgbox("Not enough CPU time to be server!\n"); + overmind.framecount+=acc-10; + acc=10; + } + } + delete menu; + + deinit_stuff(); + delete resmanager; +} diff --git a/source/recording.cpp b/source/recording.cpp new file mode 100644 index 0000000..452e178 --- /dev/null +++ b/source/recording.cpp @@ -0,0 +1,399 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include +#include "res_compress.h" +#include "game.h" +#include "config.h" +#include "canvas.h" +#include "net.h" +#include "net_buf.h" +#include "packets.h" +#include "dict.h" +#include "stringtable.h" +#include "clock.h" +#include "chat_text.h" +#include "recording.h" + +Recording *recording = NULL; +Playback *playback = NULL; + +Recording::Recording() { + res=NULL; + all_output=NULL; + all_output_size=0; +} + +Recording::~Recording() { + if(res) + delete res; + if(all_output) + free(all_output); +} + +bool Recording::create(const char *n) { + res = new Res_compress(n, RES_CREATE); + return res->exist; +} + +void Recording::write_hunk(Byte h) { + if(!res) + return; + res->write(&h, sizeof(h)); +} + +void Recording::start_for_multi(Packet* p) { + frame=0; + write_packet(p); +} + +void Recording::step() { + frame++; +} + +void Recording::write_packet(Packet* p) { + if(!res) + return; + write_hunk(11); + res->write(&frame, sizeof(frame)); + Net_buf n; + p->write(&n); + Word size=n.len(); + res->write(&size, sizeof(size)); + res->write(n.buf, size); +} + +void Recording::end_single(Canvas* c) { + if(!res) + return; + if(!c) + return; + write_hunk(3); + memset(playername, 0, sizeof(playername)); + strcpy(playername, c->name); + score=c->stats[CS::SCORE].get_value(); + lines=c->lines; + level=c->level; + res->write(playername, sizeof(playername)); + res->write(&score, sizeof(score)); + res->write(&lines, sizeof(lines)); + res->write(&level, sizeof(level)); +} + +void Recording::end_multi() { + if(!res) + return; + if(game->single) + end_single(game->net_list.get(0)); + write_summary(); + all_output=res->write_compress(&all_output_size); + delete res; + res=NULL; +} + +void Recording::write_summary() { + if(!res) + return; + Textbuf buf; + buf.append("quadra_version %i.%i.%i\n", Config::major, Config::minor, Config::patchlevel); + buf.append("duration %i\n", frame); + buf.append("time %i\n", Clock::get_time()); + game->addgameinfo(&buf); + + Dword size=buf.len(); + write_hunk(13); + res->write(&size, sizeof(size)); + res->write(buf.get(), size); +} + +Playback::Playback(Res* r): data(0, 1024) { + packet_gameserver=NULL; + auto_demo = false; + sum=NULL; + int i; + nextByte=0; + completed = false; + old_mode=false; + valid=false; + for(i = 0; i < 3; i++) { + player[i].name[0] = 0; + player[i].repeat = 2; + player[i].color = i; + player[i].shadow = 1; + player[i].smooth = 1; + } + res=r; + read_all(); +} + +Playback::~Playback() { + while(packets.size()) { + Demo_packet *dp=packets.last(); + packets.removelast(); + if(dp->p) + delete dp->p; + delete dp; + } + if(sum) + delete sum; + if(packet_gameserver) + delete packet_gameserver; +} + +Byte Playback::get_byte() { + if(!old_mode) + return 0; + if(nextByte>=data.size()) + completed=true; + if(completed) + return 0; + Byte tmp=data[nextByte++]; + if(nextByte>=data.size()) { + completed=true; + } + return tmp; +} + +bool Playback::single() { + if(old_mode) + return true; + if(packet_gameserver && packet_gameserver->single) + return true; + return false; +} + +bool Playback::check_scores(Canvas* c) { + if(strcmp(c->name, player[0].name)) + return false; + if(c->stats[CS::SCORE].get_value() != score) + return false; + if(c->lines != lines) + return false; + if(c->level != level) + return false; + return true; +} + +Byte Playback::read_hunk() { + Byte temp; + res->read(&temp, sizeof(temp)); + return temp; +} + +void Playback::read_all() { + bool got_summary=false; + bool got_info=false; + bool got_seed=false; + bool got_data=false; + bool got_packet=false; + while(res && !res->eof()) { + Byte hunk = read_hunk(); + switch(hunk) { + case 0: read_seed(); got_seed=true; break; + case 1: read_single(); break; + case 2: read_data(); got_data=true; break; + case 3: read_info(); got_info=true; break; + case 11: read_packet(); got_packet=true; break; + case 13: read_summary(); got_summary=true; break; + default: + msgbox("Playback::read_all: bad hunk %i!\n", hunk); + break; + } + } + old_mode=!got_packet; + //Single-player demos from 1.0.1 have infos but no summary so + // we build a fake summary (with real stats :)) + if(old_mode && !got_summary && got_info) { + char st[1024]; + sum = new Dict(); + sprintf(st, "players/0/name %s", player[0].name); + sum->add(st); + sprintf(st, "players/0/score %i", score); + sum->add(st); + sprintf(st, "players/0/lines %i", lines); + sum->add(st); + sprintf(st, "players/0/level %i", level); + sum->add(st); + } + valid=false; + if(got_seed && got_data && got_info) + valid=true; + if(packet_gameserver) + valid=true; +} + +void Playback::read_seed() { + res->read(&seed, sizeof(seed)); + for(int i=0; i<3; i++) { + res->read(&player[i].repeat, sizeof(player[0].repeat)); + } +} + +void Playback::read_single() { + res->read(&single_player, sizeof(single_player)); +} + +void Playback::read_data() { + data.reserve(res->size()); + Byte b; + while(!res->eof()) { + res->read(&b, sizeof(b)); + if(b==255) + break; + data.append(&b, sizeof(b)); + } +} + +void Playback::read_info() { + res->read(&player[0].name, sizeof(player[0].name)); + player[0].name[sizeof(player[0].name)-1]=0; + res->read(&score, sizeof(score)); + res->read(&lines, sizeof(lines)); + res->read(&level, sizeof(level)); +} + +void Playback::read_packet() { + Dword frame=0; + Word size=0; + Net_buf n; + res->read(&frame, sizeof(frame)); + res->read(&size, sizeof(size)); + if(size>sizeof(n.buf)) + return; + res->read(n.buf, size); + Packet *p=net->net_buf2packet(&n, true); + if(!p) + return; +/* char st[1024]; + if(net && net->net_param) { + net->net_param->print_packet(p, st); + msgbox("Playback::read_packet: %s\n", st); + }*/ + if(p->packet_id==P_GAMESERVER) { + Packet_gameserver *p2=(Packet_gameserver *) p; + if(p2->version<20 || p2->version>23) { + msgbox(" Demo game version is %i. Current version is %i. Invalid demo file\n", p2->version, Config::net_version); + res=NULL; + valid=false; + completed=true; + delete p2; + return; + } + seed = p2->game_seed; + packet_gameserver=p2; + } + else { + //Ignore packets that appear before Packet_gameserver + if(!packet_gameserver) { + delete p; + return; + } + Demo_packet *demo_packet=new Demo_packet(frame, p); + packets.add(demo_packet); + } +} + +void Playback::read_summary() { + Dword size; + res->read(&size, sizeof(size)); + Buf buf(size+1); + res->read(buf.get(), size); + buf[size]=0; + Stringtable st(buf.get(), size); + sum=new Dict(); + int i; + for(i=0; iadd(st.get(i)); + msgbox("Playback::read_summary: %s\n", st.get(i)); + } +} + +void Playback::create_game() { + if(old_mode) { + Game_params p; + p.name=""; + p.set_preset(PRESET_SINGLE); + (void)new Game(&p); + return; + } + if(!packet_gameserver) + return; + new Game(packet_gameserver); + game->wants_moves=false; +} + +Demo_packet Playback::next_packet() { + if(packets.size()) + return *packets[0]; + else + return Demo_packet(0xFFFFFFFF, NULL); +} + +void Playback::remove_packet() { + if(packets.size()) { + Demo_packet *dp=packets[0]; + packets.remove(0); + //Caller will delete the packet + delete dp; + } +} + +void Playback::shit_skipper2000(bool remove_chat) { + //This is the Shit-skipper 2000(tm) + // call this if you want to clean up a messy + // multiplayer demo: it removes innane chatter at the + // beginning and correct all packet times so that the + // game starts immediatly. Best used with auto_demo==true + Dword shit_skipper_bias=0; + bool got_pause=false; + bool got_player=false; + int i; + for(i=0; ip && dp->p->packet_id==P_PLAYER) { + if(got_pause) { + shit_skipper_bias=dp->frame; + break; + } + else + got_player=true; + } + if(dp->p && dp->p->packet_id==P_PAUSE) { + if(got_player) { + shit_skipper_bias=dp->frame; + break; + } + else + got_pause=true; + } + } + if(iframe<=shit_skipper_bias) && dp->p && dp->p->packet_id==P_CHAT) { + delete dp->p; + delete dp; + packets.remove(i); + i--; + } + else { + if(dp->frame<=shit_skipper_bias) + dp->frame=0; + else + dp->frame-=shit_skipper_bias; + } + } + } +} + +Demo_packet::Demo_packet(const Demo_packet& dp): frame(dp.frame), p(dp.p) { +} + +Demo_packet::Demo_packet(Dword pframe, Packet* pp): frame(pframe), p(pp) { +} + +Demo_packet::~Demo_packet() { +} diff --git a/source/score.cpp b/source/score.cpp new file mode 100644 index 0000000..7dd613e --- /dev/null +++ b/source/score.cpp @@ -0,0 +1,196 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "dict.h" +#include "game.h" +#include "canvas.h" +#include "score.h" + +Score::Score() { + int i; + for(i=0; inet_list.goal_stat); +} + +void Score::updateFromGame() { + if(!game) + return; + Byte team; + int i; + for(team=0; teamnet_list.get(i); + if(c && c->color==team) + count++; + } + team_goals_changed[team]=!(count==player_count[team]); + } + if(game) { + for(i=0; inet_list.get(i); + if(c) { + player_team[i]=c->color; + if(current_sortstats[current_sort]) + team_goals_changed[c->color] = true; + stats[i]=*c; + } + else { + player_team[i]=255; + stats[i].clear(); + } + } + update_team(); + } + sort(current_sort); +} + +void Score::updateFromDict(Dict *d) { + int i, tmp; + for(i=0; isize(); i++) { + const Dict *d2 = d->get_sub(i); + temp = d2->find("frags"); + if(temp) + tmp=atoi(temp); + else + tmp=0; + stats[i].stats[CS::FRAG].set_value(tmp); + temp = d2->find("deaths"); + if(temp) + tmp=atoi(temp); + else + tmp=0; + stats[i].stats[CS::DEATH].set_value(tmp); + temp = d2->find("lines"); + if(temp) + tmp=atoi(temp); + else + tmp=0; + stats[i].stats[CS::LINESTOT].set_value(tmp); + temp = d2->find("score"); + if(temp) + tmp=atoi(temp); + else + tmp=0; + stats[i].stats[CS::SCORE].set_value(tmp); + temp = d2->find("team"); + if(temp) + tmp=atoi(temp); + else + tmp=255; + player_team[i] = tmp; + } + update_team(); + sort(current_sort); +} + +void Score::update_team() { + Byte team; + for(team=0; teamo.stats[type]) + return type!=DEATH? true:false; + if(stats[type]o.stats[FRAG]) + return true; + if(stats[FRAG]o.stats[DEATH]) + return false; + if(stats[SCORE]>o.stats[SCORE]) + return true; + if(stats[SCORE]o.stats[LINESTOT]) + return true; + if(stats[LINESTOT]draw(video->vb, x, y); + if(next) { + next->draw(video->vb,x,y-18); + } +} + +void Zone_next::set_next(Bloc* n) { + next = n; + dirt(); +} + +Zone_small_next::Zone_small_next(Inter* in, const Bitmap& fond, int px, int py): + Zone_next(in, fond, px, py, 4*6, 2*6) { +} + +void Zone_small_next::draw() { + back->draw(video->vb, x, y); + if(next) { + next->small_draw(video->vb,x,y-6); + } +} + +void Zone_canvas_bloc::draw() { + canvas->setscreen(); + canvas->blit_back(); + canvas->blit_bloc(canvas->bloc_shadow); + canvas->blit_bloc(canvas->bloc); + if(canvas->color_flash) + canvas->blit_flash(); + if(canvas->level_up) + canvas->blit_level_up(); + dirt(); // this zone is always dirty +} + +Zone_canvas::Zone_canvas(Inter* in, Bitmap& bit, int px, int py, Canvas *can, int pw, int ph, bool small_watch): + Zone(in, px, py, pw, ph) +{ + fond = new Bitmap(bit[y]+x, w, h, bit.realwidth); + screen = Video_bitmap::New(x, y, w, h); + canvas = can; + if(!small_watch) { + znext = new Zone_small_next(in,bit,x+5,2); + znext2 = new Zone_small_next(in,bit,x+32,8); + znext3 = new Zone_next(in,bit,x+60,0); + zbonus = new Zone_bonus(in, x+182, y, &canvas->bonus, canvas, bit); + } else { + znext = new Zone_small_next(in,bit,x,y-30); + znext2 = new Zone_small_next(in,bit,x+24,y-25); + znext3 = new Zone_small_next(in,bit,x+48,y-20); + zbonus = new Zone_small_bonus(in, x+61, y, &canvas->bonus, canvas, bit); + } + canvas->set_canvas_pos(x, y, fond, screen, znext, znext2, znext3, inter); +} + +Zone_canvas::~Zone_canvas() { + if(zbonus) + delete zbonus; + if(znext3) + delete znext3; + if(znext2) + delete znext2; + if(znext) + delete znext; + delete screen; + delete fond; +} + +void Zone_canvas::draw() { + int i,j; + for(j = 0; j < 36; j++) + for(i = 0; i < 14; i++) + canvas->dirted[j][i]=2; +} + +Zone_combo::Zone_combo(Zone_combo **z, Canvas *c, const char *text, int px, int py): +Zone_text(c->inter, text, px, py) { + canvas = c; + zone = z; + *zone = this; +} + +Zone_combo::~Zone_combo() { + *zone = NULL; +} + +void Zone_combo::draw() { + if(canvas->inter && !canvas->small_watch) { + Zone_text::draw(); + dirt(); + canvas->dirt_rect(x-canvas->x,y-canvas->y,x+w-canvas->x,y+h-canvas->y); + } +} + +Zone_bonus::Zone_bonus(Inter* in, int px, int py, int *v, Canvas *c, const Bitmap& bit, int pw, int ph): +Zone_watch_int(in, v, px, py, pw, ph) { + canvas = c; + back = new Bitmap(bit[y]+x, w, h, bit.realwidth); +} + +Zone_bonus::~Zone_bonus() { + delete back; +} + +void Zone_bonus::draw() { + int i; + back->draw(video->vb,x,y); + bool first_done=false; + for(i=0; ibonus; i++) { + Byte side=5; //Left and right + if(game->net_version()<23) + side=15; //All sides + else { + if(!first_done) { + side |= 8; //Bottom + first_done=true; + } + if(canvas->bon[i].final) { + side |= 2; //Top + first_done=false; + } + } + raw_draw_bloc(video->vb, x, (19-i)*18+y, side, color[canvas->bon[i].color]); + } +} + +Zone_color_select::Zone_color_select(Inter* in, int *pv, int px, int py, Byte co[MAXTEAMS]): + Zone_state(in, pv, px, py, 50, 20, MAXTEAMS) +{ + for(int i=0; ivb->hline(y, x, w, 255); + for(int i=1; i < h-1; i++) + video->vb->hline(y+i, x, w, col[last_val]); + video->vb->hline(y+h-1, x, w, 0); + video->vb->vline(x, y, h, 255); + video->vb->vline(x+w-1, y, h, 0); +} + +Zone_color_select_noclick::Zone_color_select_noclick(Inter* in, int *pv, int px, int py, Byte co[MAXTEAMS]): + Zone_color_select(in, pv, px, py, co) { +} + +Zone_menu::Zone_menu(Inter* in, Bitmap* bit, int px, int py, Bitmap* bit2): + Zone_bitmap(in, bit, px, py, bit2) { + del_bit = 0; // delete pas les pointeurs Bitmap + kb_focusable = true; +} + +Zone_menu::Zone_menu(Inter* in, const char* b1, int px, int py, const char* b2): + Zone_bitmap(in, Bitmap::loadPcx(b1), px, py, Bitmap::loadPcx(b2)) +{ + del_bit = 1; // delete les 2 pointers + kb_focusable = true; +} + +Zone_menu::Zone_menu(Inter* in, const Image& b1, int px, int py, const Image& b2): + Zone_bitmap(in, new Bitmap(b1), px, py, new Bitmap(b2)) +{ + del_bit = 1; // delete les 2 pointers + kb_focusable = true; +} + +Zone_menu::Zone_menu(Inter* in, Bitmap* fond, const char* b1, int px, int py): + Zone_bitmap(in, Bitmap::loadRaw(b1), px, py) +{ + bit2_ = bit_; + + bit_ = new Bitmap((*fond)[py]+px, bit2_->width, bit2_->height, fond->realwidth); + actual = bit_; + del_bit = 1; + kb_focusable = true; +} + +Zone_menu::~Zone_menu() { + if(del_bit) { + delete bit_; + delete bit2_; + } +} + +void Zone_menu::entered() { + Sfx stmp(sons.point, 0, -2000, 0, 22000+ugs_random.rnd(2047)); + Zone_bitmap::entered(); +} + +void Zone_menu::clicked(int quel) { + Sfx stmp(sons.click, 0, -1000, 0, 14000+ugs_random.rnd(511)); + Zone_bitmap::clicked(quel); +} + +void Zone_listbox2::clicked(int quel) { + Sfx stmp(sons.enter, 0, -800, 0, 28000+ugs_random.rnd(1023)); + Zone_listbox::clicked(quel); +} + +void Zone_state_text2::clicked(int quel) { + Sfx stmp(sons.enter, 0, -800, 0, 26000+ugs_random.rnd(1023)); + Zone_state_text::clicked(quel); + notify_all(); +} + +void Zone_text_select2::entered() { + Sfx stmp(sons.enter, 0, -1000, 0, 22000+ugs_random.rnd(1023)); + Zone_text_select::entered(); +} + +void Zone_text_select2::clicked(int quel) { + Sfx stmp(sons.glass, 0, -200, 0, 14000+ugs_random.rnd(511)); + Zone_text_select::clicked(quel); +} + +void Zone_text_button2::entered() { + Sfx stmp(sons.enter, 0, -1000, 0, 22000+ugs_random.rnd(1023)); + Zone_text_button::entered(); +} + +void Zone_text_button2::clicked(int quel) { + Sfx stmp(sons.glass, 0, -200, 0, 14000+ugs_random.rnd(511)); + Zone_text_button::clicked(quel); +} + +Zone_set_key::Zone_set_key(Inter* in, int *pv, int px, int py): + Zone_state_text(in, pv, px, py) { + for(int i=0; i<256; i++) { + if(keynames[i][0] == 0) + add_string(ST_UNKNOWN); + else + add_string(keynames[i]); + } +} + +void Zone_small_canvas_bloc::draw() { + canvas->setscreen(); + canvas->small_blit_back(); + canvas->small_blit_bloc(canvas->bloc); + if(canvas->color_flash) + canvas->small_blit_flash(); + dirt(); // this zone is always dirty +} + +Zone_small_canvas::Zone_small_canvas(Inter* in, Bitmap& bit, int px, int py, Canvas *can): + Zone_canvas(in, bit, px, py, can, 6*10, 6*20, true) +{ + canvas->small_watch = true; // override le setting par defaut +} + +void Zone_small_canvas::draw() { + int i,j; + for(j = 0; j < 36; j++) + for(i = 0; i < 14; i++) + canvas->dirted[j][i]=2; +} + +Zone_small_bonus::Zone_small_bonus(Inter* in, int px, int py, int *v, Canvas *c, const Bitmap& bit): +Zone_bonus(in, px, py, v, c, bit, 6, 6*20) { +} + +void Zone_small_bonus::draw() { + int i; + back->draw(video->vb,x,y); + bool first_done=false; + for(i=0; ibonus; i++) { + Byte side=5; //Left and right + if(game->net_version()<23) + side=15; //All sides + else { + if(!first_done) { + side |= 8; //Bottom + first_done=true; + } + if(canvas->bon[i].final) { + side |= 2; //Top + first_done=false; + } + } + raw_small_draw_bloc(video->vb, x, (19-i)*6+y, side, color[canvas->bon[i].color]); + } +} diff --git a/source/zone_list.cpp b/source/zone_list.cpp new file mode 100644 index 0000000..1af2596 --- /dev/null +++ b/source/zone_list.cpp @@ -0,0 +1,11 @@ +/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- + * Copyright (c) 1998-2000 Ludus Design enr. + * All Rights Reserved. + * Tous droits réservés. + */ + +#include "zone_list.h" + +void Zone_list::deleteall() { + zone.deleteall(); +} diff --git a/textes/anglais.txt b/textes/anglais.txt new file mode 100644 index 0000000..2e36ae6 --- /dev/null +++ b/textes/anglais.txt @@ -0,0 +1,593 @@ +Player +·2 Start +Quadra +Hero +Score +Lines +Level +You got a highscore!! +"The highest score lies here" - Rem, 1999 +"To create or to join, that is the question" - Rem, 1999 +·2 Create a new game +·2 Join selected game +·2 Refresh local games +Server list: +Players in selected game: +Back ·0 +Please select a connection type: +Local game (no network) +TCP/IP Internet +Slow +Normal +Fast +Press a key... +Press a key for left... +Right... +Rotate counter clock-wise... +Down... +Rotate clock-wise... +You have been playing an unregistered version of Quadra. +In fact, you played for %i hours and %i minutes! +In fact, you played for %i minutes %i seconds. +You spent about %i seconds checking it. +Was it enjoyable? Was it incredibly fun? +Then, why not invest a couple of bucks and register? +You can go directly to our web site at this address: +http://www.ludusdesign.com +Or send us 10$ by mail at this address: +207, l'Heureux +McMasterville (QC) Canada +J3G 5G2 +And only then you shall be happy for the rest of your life. +(It will also disable that nag screen...) +Feel free to distribute Quadra to everyone you can! + +We even got a %s version, networkable with yours! +So watch out for Ludus Design products, and remember: +We do great stuff for you! +Thanks for registering Quadra! +It is your way of encouraging us +supporting and developing new projects! +Current user name registration: + + + + + +If you have problems installing or playing, please e-mail us +at this address: +support@ludusdesign.com + + +The Quadra team: +Rémi Veilleux: Programming, design, graphics +Stéphane Lajoie: Programming +Pierre Phaneuf: Linux port +Our web site: +http://www.ludusdesign.com +This is a beta version. Do NOT distribute! +Click on 'Back ·0' to return to main menu +Quadra(tm) by Ludus Design +Using Universal Game Skelton(tm) +UNREGISTERED VERSION. Please register if you like this game! +REGISTERED VERSION. Thank you! +Version number: %i +Warning: Failed to initialise sound system +No configuration file found. Using defaults. +Warning: Wrong version in configuration file. Using defaults. +Hit something to continue... +Quadra, Universal Game Skelton and the Ludus Design logo are trademarks +of Ludus Design, Copyright (C) 1998-2000. All rights reserved. +Quadra endgame statistics +For the %s team: +Statistics only available in full-release +Click on 'Back ·0' to return to main menu +Start %s +Show players +Block info +Lines info +Select scheme +Chat window +This demo release is sending %s some lines! +Close +%s sends %i line%s. +%s joined %s team. +Orange +Cyan +Red +Purple +Yellow +Green +Blue +%s died. +Score: +Lines: +Level: +Deaths: +Frags: +Create game +·2 Create game +Join game +Single +Double +Triple +Quad +5-lines +6-lines +7-lines +8-lines +9-lines +10-lines +11-lines +12-lines +13-lines +14-lines +More! +Total: +Double! %i pts +Triple! %i pts +Quad! %i pts +%i-lines! %i pts + +Select color scheme: +From level %i +Select your team: +Player(s) in team: +%s team: +No player joined +From: +To: +All team +Or enter the IP address of the server: +Looking for server name on Internet. This can take several minutes... +·2 Cancel +Unable to locate host. Please verify and try again. +Name: +Waiting to join... +TCP/IP port number (1024-65535): +Attempting to connect with game %s... +The administrator refused your request. +Please click on 'Back ·0' and select another game. +Connected. Waiting for server reply... +Game paused by %s. +server +%s fragged %s! +Game %s created. +Game %s joined. +Searching for Quadra server at address %s... +No response from server. You can continue to wait or abort this operation. +The following network error occurred: +If the error persists, please e-mail our technical support. +You will not be able to play on the Internet. +You can enter the TCP/IP address of the server instead. +Unable to establish network connection. Try another TCP/IP address. +Unable to create server. Check your network configuration. +Unable to create server. Reboot your computer and try again. +Network tools +Drop player +Drop connection +Accept players +Accept connections +Yes +No +Drop this player +%s was dropped by server! +You have been rejected. +Try another server. +Drop this connection +Select CD playing mode: +No music +Auto-change track +Loop all tracks +Game options +(Note: you must have the same port number to connect to another computer) +Comment jouer? +·2 Continue +Welcome to this interactive tutorial! +Vous pourrez quitter et retourner au menu principale à n'importe quel +moment en appuyant sur 'Echap'. Pour passer à la suite, vous pouvez +cliquez sur le mot '·2 Continuer' en bas ou appuyez sur 'Retour'. + +Quadra est essentiellement un jeu de logique et de vitesse. Plus vous +jouez rapidement, plus vous êtes en mesure "d'attaquer" vos adversaires. +En plus, vous obtenez davantage de points en fonction de la vitesse de +votre jeu. + +Allons voir un peu les élements du jeu... +Pour commencer, il faut sélectionner +un joueur parmi les 3 définis dans le +menu "Configurer joueur". Il suffit +cliquer sur le nom et la partie +débute automatiquement. +Maintenant que la partie est commencé, +plusieurs informations sont apparues. +A gauche, des compteurs vous indiquent +le nombre de blocs qui ont été déposés +depuis le début de la partie. +A droite, des compteurs vous indiquent +le nombre de lignes que vous avez fait +au cours de la partie. Nous reviendrons +sur ce sujet un peu plus tard. +En bas de l'écran de jeu ce situent les +diverses informations telles que: +le pointage actuel, le nombre de lignes +et le niveau de difficulté. +Complètement en haut, vous pouvez voir +les 3 prochains blocs qui suivront +lorsque vous aurez déposé le bloc actuel. +Celui qui est au centre et qui est plus +grand représente votre prochain bloc. +Au centre, c'est votre terrain de jeu. +C'est à l'intérieur de cette zone que +vous aurez à maneuvrer les blocs afin +de faire des lignes complètes. + +Pour contrôler le bloc, vous devez +définir les touches dans le menu +"Configurer joueur". Les contrôles +sont simples: déplacement gauche et +droite, déplacement vers le bas, et +rotations. +Notez que dans le bas de cette zone, +directement en dessous de votre bloc +se trouve une "ombre" de couleur +sombre. Celle-ci sert à aligner vos +déplacement afin de prévoir à quel +endroit déposer votre bloc. Vous +pourrez activer ou désactiver cette +option dans le menu "Configurer +joueur". +Maintenant, voyons un peu comment se déroule +le jeu en déposant quelques blocs. Ceux qui +connaissent déjà ce type de jeu connaissent donc +la base de Quadra. Pour les autres, il vous +restera à maitriser ces techniques pour ensuite +passer aux choses sérieuses. +Le principe de base est simple: +déposé les blocs le plus bas possible +et tenter des les emboiter afin de ne +pas laisser de "trou" entre eux. +Quand vous avez une ligne complète, +celle-ci s'efface et tout les blocs +qui étaient au-dessus tombent dans +l'espace qui s'est libéré. +Regardez attentivement: le bloc qui +va être déposé ici va compléter une +ligne entière. Vous pouvez le +constatez en regardant l'ombre du +bloc qui s'emboite entre les autres. +La ligne qui vient d'être complétée +s'appelle un "simple" car ce n'était +qu'une seule ligne. Avec l'expérience, +vous pourrez faire des doubles, +triples, quadruples, et même plus! +Regardons un autre exemple... +Attention, si vous remarquez bien, +ce bloc devrait nous donner un +quadruple car il complète 4 lignes +d'un seul coup! +Le mot qui s'élève dans l'écran +sert à mentionner que vous venez +de faire un bon coup. + +Nettoyons un peu cette aire de jeu... +Un autre petit "quad", simplement +fait en glissant une barre verticale +dans la colonne libre. +Maintenant, passons aux choses sérieuses: les +réactions en chaîne. Grâce à ces réactions, vous +pouvez faire plus que 4 lignes d'un coup. Etant +donné que le plus long bloc n'a que 4 unités de +hauteur (la barre verticale), vous devrez donc +utiliser une astuce supplémentaire. Il sagit de +positionner des blocs en prévision de les faire +tomber plus tard, afin qu'ils complètent des +lignes à leur tour, et ainsi de suite. +Voici comment. + +Les blocs dans Quadra sont attirés par la +gravité, donc vers le bas. Cependant, ils sont +solides et il faut les "casser" pour pouvoir +les faire tomber et causer une réaction en +chaîne. +Nous allons vous montrer un exemple avec la +colonne de gauche, en y déposant 2 barres +verticales, que nous ferons tomber par la suite +afin de faire plusieurs lignes (et par +conséquent, beaucoup de points!) +Vous voyez le bloc en "L" rose? Il +sert à empêcher la barre verticale de +tomber dans le trou. Il faudra le +casser pour déclencher notre réaction. +Pour se faire, il sagit d'éliminer la +ligne horizontale qui se trouve à la +base du "L"... +Observez bien. Le "L" rose qui est +déposé ici à droite va nous donner +une ligne complète. Cette ligne est +exactement celle qu'il nous faut +pour casser l'autre "L" rose à gauche +qui sert à retenir nos 2 barres +verticales. +Observons au ralenti... +Et voilà! 7 lignes d'un seul coup! +Vous noterez que théoriquement nous +aurions pu faire 10 lignes. (Car nous +avions 2 barres verticales plus un +morceau de "L") mais il y avait des +trous dans notre construction. +L'image de fond a changer: ceci +indique que nous avons changer de +niveau en même temps. +Il ne vous reste qu'à vous pratiquez. Voici +d'autres informations pertinentes: +- Vous changez de niveau de difficulté à chaque + fois que vous faites 15 lignes. Le jeu sera + donc de plus en plus rapide. +- Vous perdez la partie quand vos blocs atteignent + le centre en haut de l'aire de jeu. Cependant, + vous pouvez avoir des blocs qui débordent sur + les côtés sans problème. +Vous pouvez vous pratiquer en jouant des parties solo. Mais l'essence du +jeu réside dans la compétition avec vos amis. Le mode multi-joueur est +similaire au mode solo. Cependant, vous pouvez continuer de jouer même +si vous êtes "mort", sans perdre vos totaux de lignes et de blocs. Cela +ne fait qu'augmenter votre compteur de "mort". De plus, chaque fois que +vous ferez un "double" ou plus, vous attaquerez vos adversaires! Ceci +est fait en envoyant des lignes dans le bas de leur écran, les forçant +à réagir, sans quoi ils seront envahis jusqu'au cou. + +Vous sélectionnez votre couleur, ce qui désigne votre équipe. D'ailleurs, +vous pourrez voir l'équipe qui vous a envoyé des lignes et regardant la +couleur des lignes reçues: elles seront de la couleur de l'équipe en +question. + +Ceci met fin à ce tuteur. Allez vous pratiquer un peu! +Connected. Downloading game information... +Show: +Frags&Deaths +Score +Lines +Blocks +Unable to join because +name already exists. +*** %s dropped an invalid block! *** +Test ping +Remote player watch +(update per second) +Ping in millisecond: +Average: +Watch: +Full-size +4 at a time +Watch selected +Quit ·0 +·2 Check IP info +IP configuration of this computer +Host name: +IP address: +IP info +Faster +Network version number: %i +* older version * +* newer version * +Resume %s +Fragged %s! +Clean Canvas!! +The host responding is not a Quadra server. +Please select another server, or change your port number. +The Quadra server is using an older version (%i). +The server must be updated to your version %i. +The Quadra server is using a more recent version (%i). +Please check for Quadra update at our web site 'www.ludusdesign.com'. +Address book +Connect +Looking for %s... +Type: +Free for all +Survivor +The last team alive wins, then everybody respawn. +Frag your opponents and respawn when you die. +Level up: +At 15 lines +Disabled +Start game at level: +%s is the winner! +Quadra - Command line + + + + + + + +Frags +Deaths +Playback +Slow motion +Fast forward +Smallest offensive line combo: + +Restart +%s has left the game. +·2 Game will start in 5 seconds... +·2 Game starts! +·2 The server has been disconnected. +Replay last single-player game: +·2 End game +·2 Game terminates. +You cannot join now: +this game is terminated. +The game ends when: +Never +Frags +Time +frags. +minutes. +Mouse cursor speed (1-255): +·2 Address book +·2 Refresh Internet games +Searching for public Internet games... +Public: +·2 Save settings +Public game server address: +Advanced options: +Invalid game server address. +Go to the "Option" menu and enter a valid address. +Default server: http://ludusdesign.com/cgibin/qserv.pl +Quadra version %i.%i.%i +NOT REGISTERED +No Quadra server found at specified address. +Either the server is down, or this address isn't a Quadra game server. +·2 Invalid game server response. +·2 Aborting: Game won't be public. +·2 Network error while looking for game server: %s. +How to register Quadra +Quadra is a freely distributable software. You need to register to encourage +us providing you with other quality game and support of Quadra. +You may obtain your registration key on-line with your credit card +by visiting this Internet address: + + + +http://www.ludusdesign.com/quadra/register.html +Enter your first and last name, followed by the password that you've received: +·2 Click here to register +Thank you! We hope you'll enjoy Quadra! +Select display language: +English +French +Sylvain Fortin: Glue +Véronique Bruneau: Graphics +Name: +Password: +Invalid password entered. Please verify your input and try again... +%i seconds remaining! +·2 Next page + + + +Updating public game server +One moment please... + + + +World highscores +·2 Update +Connection accepted from [%s] +Connection closed from [%s] +Click on ·2 Update to update worldwide highscores. +Connecting. Please wait... +Error: the server returned [%s]. +Transfer completed successfully. +Connected. Transfer in progress. %i bytes. +Transfer interrupted by user. +No Quadra game server found. +[No name] +Status: +Not yet started +Playing +Terminated +TCP/IP Local network (LAN) + + + +%s has joined back! +The connection with the server has been lost. +Try connecting again, or select another server. +·2 Start playback +The Demo Central +Current directory: +File size: +Date & time: +Version: +Duration: +Game type: +Game end: +File '%s' is not a Quadra demo file +Single player +Unknown +Never +After %i frags +After %i minutes +Players +·2 Demo completed +Record game demo: +·2 Unable to record game '%s' +·2 Recording game as '%s' +Game name: +%s team +%i minutes +%i seconds +·2 Auto-watch +You cannot join now: +Game is already full! +%s team + is %i frags from victory! +Game tied! + is 1 frag from victory! +Clean canvas: %s sends %i lines! +Clean canvas: %s sends 1 line! +Hot potato +Clear lines to get rid of the hot potato! + + + +Select handicap: +Beginner +Apprentice +Normal +Master +Grand Master +Delete demo + wins the game! + gets the hot potato! +Clear %i lines. +Peace +Points +No lines sent, no frags, no violence. +points. +lines. +After %i points +After %i lines +frag +point +line + is %i %s from victory! +Lines +Clean canvas: %s clears %i lines! +Clean canvas: %s clears 1 line! +%s clears %i line%s. + got rid of the hot potato! +·2 Restart game +·2 Rejoin game +Goal (%s%s): +You get the + hot potato! +You got rid of + the hot potato! +ngStats log: +ngWorldStats log: +gdesc_en.txt +Blind +Waiting for the + next round... +Press a key to + restart. +%s wins the round! +Nobody +·2 Unable to log game '%s' +·2 Logging game as '%s' +Points per min. +Blocks per min. +Rotate twice... +Instant drop... +Sprint +Select game type: +Normal +Sprint (5 minutes) +Avg. attack +Avg. combo +Rank +Speed +Combos \ No newline at end of file diff --git a/textes/francais.txt b/textes/francais.txt new file mode 100644 index 0000000..9e5bd12 --- /dev/null +++ b/textes/francais.txt @@ -0,0 +1,593 @@ +Joueur +·2 Démarrer +Quadra +Héros +Score +Lignes +Niveau +T'es dans la liste!! +"Les meilleurs reposent ici..." - Rem, 1999 +"Créer ou joindre, là est la question" - Rem, 1999 +·2 Créer une partie +·2 Joindre la partie sélectionnée +·2 Rafraîchir parties locales +Liste des serveurs: +Joueurs dans la partie sélectionnée: +Retour ·0 +Sélectionnez un type de connexion: +Mode local (pas de réseau) +TCP/IP Internet +Lent +Normal +Rapide +Appuyez sur une touche... +Enfoncez la touche pour gauche... +Droite... +Tourner sens anti-horaire... +En bas... +Tourner sens horaire... +Vous venez de jouer avec une version non enregistrée. +En fait, vous avez joué pendant %i heures et %i minutes! +En fait, vous avez joué pendant %i minutes %i secondes. +Vous avez seulement passé %i secondes à l'essayer. +Est-ce que c'était intéressant? Ou même fabuleux? +Si oui, alors pourquoi ne pas y investir quelques sous! +Vous pourrez procéder à l'enregistrement via notre site Web à l'adresse: +http://www.ludusdesign.com +Ou si vous préférez, envoyez-nous 10$ par la poste à l'adresse: +207, l'Heureux +McMasterville (QC) Canada +J3G 5G2 +Et alors, vous deviendrez heureux pour le reste de votre vie. +(ça désactivera aussi le message au début du jeu...) +N'oubliez pas de copier ce jeu à tout le monde! + +Nous avons également une version %s compatible avec la vôtre! +Alors tenez-vous à l'affût des jeux de Ludus Design, et rappelez-vous: +On fait des jeux hots, pour vous! +Merci d'avoir enregistré Quadra! +Vous nous encouragez ainsi à continuer d'améliorer et +à développer d'autres projets dans l'avenir! +L'enregistrement est au nom de: + + + + + +Si vous avez des problèmes d'utilisation ou des questions, écrivez-nous +à cette adresse: +support@ludusdesign.com + + +L'équipe de Quadra: +Rémi Veilleux: Programmation, design, graphisme +Stéphane Lajoie: Programmation +Pierre Phaneuf: Conversion Linux +Notre site Web: +http://www.ludusdesign.com/index_fr.html +Ceci est une version démo gratuite et distribuable. +Cliquez sur 'Retour ·0' pour revenir au menu principal +Quadra(mc) Ludus Design +Utilise le "Universal Game Skelton(mc)" +VERSION NON ENREGISTRÉE. S.V.P. enregistrez si vous aimez ce jeu! +VERSION ENREGISTRÉE. Merci! +Version numéro: %i +Avertissement: Impossible d'initialiser la carte de son +Fichier de configuration inexistant. Il sera créé. +Avertissement: Mauvaise version du fichier de configuration. Il sera effacé. +Appuyez sur quelque chose pour continuer... +Quadra, Universal Game Skelton et le logo Ludus Design sont des marques +de commerce de Ludus Design, Copyright (C) 1998-2000. Tous droits réservés. +Quadra - Statistiques de fin de partie +Pour l'équipe %s: +Les statistiques ne sont disponibles que dans la version complète. +Cliquez sur 'Retour ·0' pour retourner au menu +Démarre %s +Affiche joueurs +Info blocs +Info lignes +Choisir thème +Fenêtre Chat +Cette version démo envoie des lignes à %s! +Fermer +%s envoie %i ligne%s. +%s a joint l'équipe %s. +Orange +Turquoise +Rouge +Pourpre +Jaune +Vert +Bleu +%s a trépassé. +Score: +Lignes: +Niveau: +Morts: +Frags: +Création d'une partie +·2 Créer la partie +Joindre une partie +Simple +Double +Triple +Quad +5-lignes +6-lignes +7-lignes +8-lignes +9-lignes +10-lignes +11-lignes +12-lignes +13-lignes +14-lignes +Plus! +Total: +Double! %i pts +Triple! %i pts +Quad! %i pts +%i-lignes! %i pts + +Choisir le thème: +Du niveau %i +Choisir l'équipe: +Joueur(s) dans l'équipe: +Equipe %s: +Aucun joueur à date... +De: +A: +Tous +Ou taper ici l'adresse IP à vérifier: +Cherche du nom sur l'Internet. Ceci peut prendre quelques minutes... +·2 Annuler +Impossible de trouver ce nom. Vérifier que vous l'avez bien écrit. +Nom: +En attente pour joindre... +Numéro de port TCP/IP (1024-65535): +Connexion en cours avec la partie %s... +L'administrateur de partie a refusé votre demande. +Appuyez sur 'Retour ·0' et sélectionnez une autre partie. +Connecté. Attente de la réponse du serveur... +Partie pausée par %s. +le serveur +%s a fraggé %s! +Partie %s créée. +Partie %s jointe. +Recherche d'un serveur Quadra à l'adresse %s... +Le serveur ne répond pas. Vous pouvez continuer d'attendre ou annuler l'opération. +L'erreur de réseau suivante est survenue: +Si l'erreur persiste, contactez notre support technique par e-mail. +Vous ne pourrez pas jouer en réseau par Internet. +Vous pouvez entrer directement l'adresse TCP/IP du serveur. +Impossible d'établir la connexion au serveur. Essayez une autre adresse réseau. +Impossible de créer le serveur. Vérifiez votre configuration réseau. +Impossible de créer le serveur. Redémarrez l'ordinateur et essayez de nouveau. +Fonctions réseau +Ferme joueur +Ferme connexion +Accepte joueurs +Accepte connexions +Oui +Non +Élimine ce joueur +%s a été éliminé par le serveur! +Vous avez été refusé. +Essayez un autre serveur. +Élimine connexion +Sélectionnez le mode de musique DC: +Pas de musique +Auto-change de piste +Joue toutes les pistes +Options +(Vous devez avoir le même port pour vous connecter à un autre ordinateur) +Comment jouer? +·2 Continuer +Bonjour et bienvenue dans ce tuteur interactif. +Vous pourrez quitter et retourner au menu principale à n'importe quel +moment en appuyant sur 'Echap'. Pour passer à la suite, vous pouvez +cliquez sur le mot '·2 Continuer' en bas ou appuyez sur 'Retour'. + +Quadra est essentiellement un jeu de logique et de vitesse. Plus vous +jouez rapidement, plus vous êtes en mesure "d'attaquer" vos adversaires. +En plus, vous obtenez davantage de points en fonction de la vitesse de +votre jeu. + +Allons voir un peu les éléments du jeu... +Pour commencer, il faut sélectionner +un joueur parmi les 3 définis dans le +menu "Configurer joueur". Il suffit +cliquer sur le nom et la partie +débute automatiquement. +Maintenant que la partie est commencé, +plusieurs informations sont apparues. +A gauche, des compteurs vous indiquent +le nombre de blocs qui ont été déposés +depuis le début de la partie. +A droite, des compteurs vous indiquent +le nombre de lignes que vous avez fait +au cours de la partie. Nous reviendrons +sur ce sujet un peu plus tard. +En bas de l'écran de jeu ce situent les +diverses informations telles que: +le pointage actuel, le nombre de lignes +et le niveau de difficulté. +Complètement en haut, vous pouvez voir +les 3 prochains blocs qui suivront +lorsque vous aurez déposé le bloc actuel. +Celui qui est au centre et qui est plus +grand représente votre prochain bloc. +Au centre, c'est votre terrain de jeu. +C'est à l'intérieur de cette zone que +vous aurez à manoeuvrer les blocs afin +de faire des lignes complètes. + +Pour contrôler le bloc, vous devez +définir les touches dans le menu +"Configurer joueur". Les contrôles +sont simples: déplacement gauche et +droite, déplacement vers le bas, et +rotations. +Notez que dans le bas de cette zone, +directement en dessous de votre bloc +se trouve une "ombre" de couleur +sombre. Celle-ci sert à aligner vos +déplacement afin de prévoir à quel +endroit déposer votre bloc. Vous +pourrez activer ou désactiver cette +option dans le menu "Configurer +joueur". +Maintenant, voyons un peu comment se déroule +le jeu en déposant quelques blocs. Ceux qui +connaissent déjà ce type de jeu connaissent donc +la base de Quadra. Pour les autres, il vous +restera à maitriser ces techniques pour ensuite +passer aux choses sérieuses. +Le principe de base est simple: +déposé les blocs le plus bas possible +et tenter des les emboiter afin de ne +pas laisser de "trou" entre eux. +Quand vous avez une ligne complète, +celle-ci s'efface et tout les blocs +qui étaient au-dessus tombent dans +l'espace qui s'est libéré. +Regardez attentivement: le bloc qui +va être déposé ici va compléter une +ligne entière. Vous pouvez le +constatez en regardant l'ombre du +bloc qui s'emboite entre les autres. +La ligne qui vient d'être complétée +s'appelle un "simple" car ce n'était +qu'une seule ligne. Avec l'expérience, +vous pourrez faire des doubles, +triples, quadruples, et même plus! +Regardons un autre exemple... +Attention, si vous remarquez bien, +ce bloc devrait nous donner un +quadruple car il complète 4 lignes +d'un seul coup! +Le mot qui s'élève dans l'écran +sert à mentionner que vous venez +de faire un bon coup. + +Nettoyons un peu cette aire de jeu... +Un autre petit "quad", simplement +fait en glissant une barre verticale +dans la colonne libre. +Maintenant, passons aux choses sérieuses: les +réactions en chaîne. Grâce à ces réactions, vous +pouvez faire plus que 4 lignes d'un coup. Etant +donné que le plus long bloc n'a que 4 unités de +hauteur (la barre verticale), vous devrez donc +utiliser une astuce supplémentaire. Il s'agit de +positionner des blocs en prévision de les faire +tomber plus tard, afin qu'ils complètent des +lignes à leur tour, et ainsi de suite. +Voici comment. + +Les blocs dans Quadra sont attirés par la +gravité, donc vers le bas. Cependant, ils sont +solides et il faut les "casser" pour pouvoir +les faire tomber et causer une réaction en +chaîne. +Nous allons vous montrer un exemple avec la +colonne de gauche, en y déposant 2 barres +verticales, que nous ferons tomber par la suite +afin de faire plusieurs lignes (et par +conséquent, beaucoup de points!) +Vous voyez le bloc en "L" rose? Il +sert à empêcher la barre verticale de +tomber dans le trou. Il faudra le +casser pour déclencher notre réaction. +Pour se faire, il s'agit d'éliminer la +ligne horizontale qui se trouve à la +base du "L"... +Observez bien. Le "L" rose qui est +déposé ici à droite va nous donner +une ligne complète. Cette ligne est +exactement celle qu'il nous faut +pour casser l'autre "L" rose à gauche +qui sert à retenir nos 2 barres +verticales. +Observons au ralenti... +Et voilà! 7 lignes d'un seul coup! +Vous noterez que théoriquement nous +aurions pu faire 10 lignes. (Car nous +avions 2 barres verticales plus un +morceau de "L") mais il y avait des +trous dans notre construction. +L'image de fond a changer: ceci +indique que nous avons changer de +niveau en même temps. +Il ne vous reste qu'à vous pratiquez. Voici +d'autres informations pertinentes: +- Vous changez de niveau de difficulté à chaque + fois que vous faites 15 lignes. Le jeu sera + donc de plus en plus rapide. +- Vous perdez la partie quand vos blocs atteignent + le centre en haut de l'aire de jeu. Cependant, + vous pouvez avoir des blocs qui débordent sur + les côtés sans problème. +Vous pouvez vous pratiquer en jouant des parties solo. Mais l'essence du +jeu réside dans la compétition avec vos amis. Le mode multi-joueur est +similaire au mode solo. Cependant, vous pouvez continuer de jouer même +si vous êtes "mort", sans perdre vos totaux de lignes et de blocs. Cela +ne fait qu'augmenter votre compteur de "mort". De plus, chaque fois que +vous ferez un "double" ou plus, vous attaquerez vos adversaires! Ceci +est fait en envoyant des lignes dans le bas de leur écran, les forçant +à réagir, sans quoi ils seront envahis jusqu'au cou. + +Vous sélectionnez votre couleur, ce qui désigne votre équipe. D'ailleurs, +vous pourrez voir l'équipe qui vous a envoyé des lignes et regardant la +couleur des lignes reçues: elles seront de la couleur de l'équipe en +question. + +Ceci met fin à ce tuteur. Allez vous pratiquer un peu! +Connecté. Chargement des informations de la partie... +Affiche: +Frags & Morts +Score +Lignes +Blocs +Impossible de joindre car +votre nom est déjà utilisé. +*** %s a déposé un bloc invalide!*** +Tester "ping" +Joueur réseau observé +(m.a.j. par seconde) +Ping en milli-seconde: +Moyenne: +Observe: +Pleine grandeur +4 à la fois +Observe sélectionnés +Quitte ·0 +·2 Consulter info IP +Information IP de cet ordinateur +Nom de l'hôte: +Adresse(s) IP: +Info IP +Turbo +Version réseau numéro: %i +* ancienne version * +* nouvelle version * +Continue %s +Tué %s! +Canevas Vide!! +L'ordinateur hôte n'est pas un serveur Quadra. +Veuillez essayer un autre serveur, ou changer votre numéro de port. +Le serveur Quadra utilise une version antérieure (%i) à la vôtre. +Ce serveur devra être mis à jour à votre version %i. +Le serveur Quadra utilise une version plus récente (%i) que la vôtre. +Veuillez consulter notre site web pour les mises à jour 'www.ludusdesign.com'. +Carnet d'adresses +Connecter +Recherche de %s... +Type: +Partie libre +Dernier survivant +La dernière équipe vivante marque un point, puis ça recommence. +Tuez vos adversaires et ressuscitez si vous crevez. +Changement de niveau: +Aux 15 lignes +Aucun +Débuter le jeu au niveau: +%s est le gagnant! +Quadra - Ligne de commande + + + + + + + +Frags +Morts +Visionner +Vitesse lente +Vitesse rapide +Nombre de lignes pour attaquer: + +Recommencer +%s a quitté la partie. +·2 La partie débute dans 5 secondes... +·2 C'est parti! +·2 Le serveur a été déconnecté. +Rejouer la dernière partie solo: +·2 Fin de partie +·2 La partie est terminée. +Impossible de joindre: +Cette partie est terminée. +La partie se termine lorsque: +Jamais +Frags +Temps +frags. +minutes. +Vitesse du curseur de la souris (1-255): +·2 Carnet d'adresses +·2 Rafraîchir parties sur Internet +Recherche des parties publiques sur Internet... +Publique: +·2 Enregistre ces paramètres +Adresse du serveur de partie publique: +Options avancées: +Adresse de serveur de partie invalide. +Veuillez retourner au menu "Option" et vérifiez. +Adresse par défaut: http://ludusdesign.com/cgibin/qserv.pl +Quadra version %i.%i.%i +NON ENREGISTRÉ +Aucun serveur de partie Quadra à cette adresse. +Le serveur peut être en panne, ou l'adresse n'est pas un serveur Quadra. +·2 Réponse invalide du serveur de partie. +·2 Abandon. Partie non publique. +·2 Erreur réseau en cherchant le serveur de partie: %s. +Comment enregistrer Quadra +Quadra est gratuitement et librement copiable. Vous devez enregistrer pour +nous encourager à produire d'autres jeux de qualité et pour assurer +le support de Quadra. Pour utilisez le paiement par carte de crédit, +allez visiter cette adresse Internet: + + + +http://www.ludusdesign.com/quadra/register_fr.html +Entrez votre prénom et nom, et le mot de passe que vous avez obtenu: +·2 Cliquer ici pour l'enregistrement +Merci beaucoup! Amusez-vous bien! +Choisissez la langue d'affichage: +Anglais +Français +Sylvain Fortin: Colle +Véronique Bruneau: Graphisme +Nom: +Mot de passe: +Mot de passe invalide. S.V.P. vérifiez les renseignements et réessayez... +%i secondes restantes! +·2 Change page +Paramètre manquant pour ligne de commande '%s' + + +Mise à jour du serveur de partie publique +Un instant S.V.P... + + + +Records mondiaux +·2 Valider +Connexion acceptée de [%s] +Connexion fermée de [%s] +Appuyez sur ·2 Valider pour vérifier les pointages mondiaux. +Connexion en cours. Veuillez patientez... +Erreur: le serveur a répondu [%s]. +Transfert complété avec succès. +Connecté. Transfert en cours. %i octets. +Transfert interrompu par l'usager. +Aucun serveur Quadra n'a pu être trouvé. +[Sans nom] +État: +Pas commencée +En cours +Terminée +TCP/IP Réseau local (LAN) + + + +%s est de retour! +La connexion avec le serveur a été rompue. +Essayez de connecter de nouveau, ou tentez un autre serveur. +·2 Visionner démo +La centrale de démo +Répertoire en cours: +Taille du fichier: +Date & heure: +Version: +Durée: +Type de partie: +Termine: +Le fichier '%s' n'est pas une démo Quadra. +Mode solo +Inconnu +Jamais +Après %i frags +Après %i minutes +Joueurs +·2 Demo terminée +Enregistre la partie (demo): +·2 Impossible d'enregistrer sous le nom '%s' +·2 Enregistrement de la partie dans '%s' +Nom de partie: +équipe %s +%i minutes +%i secondes +·2 Auto-observe +Impossible de joindre: +La partie est pleine! +L'équipe %s + est à %i frags de la victoire! +Egalité! + est à 1 frag de la victoire! +Canevas vide: %s envoie %i lignes! +Canevas vide: %s envoie 1 ligne! +Patate chaude +Effacez des lignes pour vous débarrasser de la patate chaude! + + + +Choisir handicap: +Débutant +Apprenti +Normal +Maître +Grand Maître +Effacer démo + gagne la partie! + reçoit la patate chaude! +Effacer %i lignes. +Paisible +Points +Pas de lignes envoyées, pas de frags, pas de violence. +points. +lignes. +Après %i points +Après %i lignes +frag +point +ligne + est à %i %s de la victoire! +Lignes +Canevas vide: %s efface %i lignes! +Canevas vide: %s efface 1 ligne! +%s efface %i ligne%s. + se débarrasse de la patate chaude! +·2 Redémarre la partie +·2 Retourne dans la partie +But (%s%s): +Vous recevez la + patate chaude! +Vous vous débarrassez + de la patate chaude! +ngStats log: +ngWorldStats log: +gdesc_fr.txt +Aveugle +En attente pour la + prochaine manche... +Appuyez sur une + touche pour repartir. +%s gagne la manche! +Personne ne +·2 Impossible de tracer sous le nom '%s' +·2 Trace de la partie dans '%s' +Points par min. +Blocks par min. +Tourne double... +Dépose immédiatement... +Sprint +Choisir le mode de jeu: +Normal +Sprint (5 minutes) +Attaque moy. +Combo moy. +Rang +Vitesse +Combos \ No newline at end of file diff --git a/textes/gdesc_en.txt b/textes/gdesc_en.txt new file mode 100644 index 0000000..b6c518e --- /dev/null +++ b/textes/gdesc_en.txt @@ -0,0 +1,50 @@ +Attack quickly and strongly to earn frags! + +If you die, press any of your control keys +to play again. + + + + + +10 +Glory to the last man standing! + +The game is divided in rounds. A round ends +when only one team has any member alive. Then, +everybody restarts with the same series of +pieces on a clean canvas. + +If you die, you will have to wait until the +beginning of the next round to play again. +20 +Enter a breakneck race for points! + +There is no way to attack your opponents, so +the goal is to score as many points as possible. + + + + + +30 +Attack strongly to confuse your opponents! + +Instead of receiving lines from your +attacks, your opponents will receive +blind pieces. Those pieces will stay +invisible for 5 seconds after being +placed. + + +40 +Clear lines to get rid of the hot potato! + +The team holding the hot potato receives all the +attacks from everybody else without being able to +fight back so it must get rid of the potato as +fast as possible. + +Each team gets the hot potato in turn and must make a +certain amount of "attack" lines to get rid of it. +50 diff --git a/textes/gdesc_fr.txt b/textes/gdesc_fr.txt new file mode 100644 index 0000000..ef1db1d --- /dev/null +++ b/textes/gdesc_fr.txt @@ -0,0 +1,50 @@ +Attaquez rapidement et violemment pour gagner des frags! + +Si vous mourrez, appuyez sur une de vos touches +de contrôle pour jouer à nouveau. + + + + + +10 +Gloire au dernier survivant! + +La partie est divisée en manches. Une manche se termine +lorsqu'une seule équipe a des joueurs toujours en vie. +Alors, tout le monde redémarre avec la même série de blocs +sur un canevas vide. + +Si vous mourrez, vous devrez attendre jusqu'au +début de la manche suivante pour jouer à nouveau. +20 +Participez à une course effrenée pour les points! + +Il n'y a aucun moyen d'attaquer vos ennemis, alors +le but est de gagner le plus de points possible. + + + + + +30 +Attaquez fortement pour confondre vos opposants! + +Au lieu de recevoir des lignes dues à vos +attaques, vos opposants recevront des blocs +aveugles. +Ces blocs resteront invisibles pour 5 secondes +après avoir été placés. + + +40 +Effacez des lignes pour vous débarrasser de la patate chaude! + +L'équipe tenant la patate chaude reçoit toutes les lignes +d'attaque des autres sans avoir la possibilité de répliquer. +Elle doit donc de débarrasser de la patate aussi tôt que +possible. + +Chaque équipe reçoit la patate chaude tour à tour et doit faire +un certain nombre de lignes d'"attaque" pour s'en débarrasser. +50 diff --git a/textes/help_en.txt b/textes/help_en.txt new file mode 100644 index 0000000..481994b --- /dev/null +++ b/textes/help_en.txt @@ -0,0 +1,27 @@ +-help Display this message. +-connect Start a multi-player game by connecting to . +-server Create a multi-player game server. +-dedicated Create a dedicated server. +-admin Set remote admin password. +-exec Execute each line of as an admin command. +-once Host a single game, no auto-restart (-dedicated). +-name Set game name. +-ffa "Free-for-all" game type (default). +-survivor "Survivor" game type. +-hotpotato "Hot potato" game type. +-peace "Peace" game type. +-levelup Enable level changes every 15 lines. +-nolevelup Disable level changes every 15 lines (default). +-level Start players on level (1..9). +-nohandicap Prohibit players from setting a handicap. +-endfrag Game will end after frags (1..9999). +-endtime Game will end after minutes (1..9999). +-endpoints Game will end after Kpoints (1..99999). +-endlines Game will end after lines (1..99999). +-public Make this game public. +-port

Listen on specified port (server only). + +Miscellaneous options: +-debug Enable debug mode +-play Play a recorded demo file +-record Record a demo file diff --git a/textes/help_fr.txt b/textes/help_fr.txt new file mode 100644 index 0000000..00a66b6 --- /dev/null +++ b/textes/help_fr.txt @@ -0,0 +1,27 @@ +-help Affiche ce message. +-connect Démarre une partie multi-joueur en connectant à . +-server Démarre un serveur de partie multi-joueur. +-dedicated Démarre un serveur dédié. +-admin Spécifie le mot de passe d'administration à distance. +-exec Exécute chaque lignes de en mode administration. +-once Joue une seule partie, pas de redémarrage (-dedicated). +-name Spécifie le nom de la partie. +-ffa Mode de jeu "Partie libre" (défaut). +-survivor Mode de jeu "Dernier survivant". +-hotpotato Mode de jeu "Patate chaude". +-peace Mode de jeu "Paisible". +-levelup Active le changement de niveau aux 15 lignes. +-nolevelup Désactive le changement de niveau aux 15 lignes (défaut). +-level Démarre les joueurs au niveau . +-nohandicap Interdit le choix d'un handicap. +-endfrag La partie se termine après frags (1..9999). +-endtime La partie se termine après minutes (1..9999). +-endpoints La partie se termine après Kpoints (1..99999). +-endlines La partie se termine après lignes (1..99999). +-public Rend cette partie publique (visible de l'Internet). +-port

Ouvre le port specifié (serveur seulement). + +Options diverses: +-debug Mode debug +-play Joue un fichier démo enregistré +-record Enregistre une démo