diff --git a/Rules.make b/Rules.make index 4f85f6d84..b1b43bf6d 100644 --- a/Rules.make +++ b/Rules.make @@ -1,6 +1,6 @@ ## ___________ _________ _____ __ ## \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ -## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \ __\ __\ ## | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | ## \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| ## \/ \/ \/ \/ \/ @@ -8,244 +8,71 @@ ## T H E W A R B E G I N S ## FreeCraft - A free fantasy real time strategy game engine ## -## Rules.make - Make RULES (GNU MAKE). -## -## (c) Copyright 1998-2000 by Lutz Sammer -## -## $Id$ -## -############################################################################ -# Configurable: -# Choose what you want to include and the correct -# version. Minimal is now the default. -############################################################################ +# Compile commands +CC=gcc +RM=rm -f +MAKE=make -# Uncomment next to get guile with gtk (and choose your gtklib version) - -#GUILEGTK = -DGUILE_GTK $(shell gtk-config --cflags) -#GUILEGTK = -DGUILE_GTK -I/usr/X11R6/include -I/usr/lib/glib/include -#GUILEGTKLIB = -lguilegtk-1.0 $(shell gtk-config --libs) -#GUILEGTKLIB = -lguilegtk-1.2 $(shell gtk-config --libs) -#GUILEGTKLIB = -L/opt/gnome/lib -lguilegtk-1.1 \ - $(shell /opt/gnome/bin/gtk-config --libs) - -#------------------------------------------------------------------------------ - -# Uncomment next for a version with guile (GNU scheme interpreter) -# (and choose your guile version) - -# guile 1.2 old version -# If you have unreferenced "gh_vector_ref" define LIBGUILE12 -#GUILE = -DLIBGUILE12 -DUSE_CCL $(GUILEGTK) -#GUILELIB = $(GUILEGTKLIB) -lguile -ldl - -# guile 1.3 latest version -# -lreadline -lncurses are needed with the distribution SuSe 5.3 -#GUILE_CFLAGS = $(shell guile-config compile) -#GUILE = -DUSE_CCL $(GUILEGTK) $(GUILE_CFLAGS) -#GUILELIB = $(GUILEGTKLIB) -lguile -lqthreads -ldl -lm -#GUILELIB = $(GUILEGTKLIB) $(shell guile-config link) -lreadline -lncurses -#GUILELIB = $(GUILEGTKLIB) $(shell guile-config link) - -# guile -#CCL = $(GUILE) -#CCLLIB = $(GUILELIB) - -#------------------------------------------------------------------------------ -# Comment next for a version without SIOD (scheme interpreter) -# - -# Try to use siod (currently default) -CCL = -DUSE_CCL2 -CCLLIB = -lm - -#------------------------------------------------------------------------------ - -# Uncomment next to add threaded sound support -# You should have a thread safe X11 (libc6 or glibc) - -#THREAD = -D_REENTRANT -DUSE_THREAD -#THREADLIB = -lpthread - -#------------------------------------------------------------------------------ - -# Choose correct version of glib (needed for gtk) - -# Where do you have installed your glib? -#GLIB = -DUSE_GLIB -I/usr/lib/glib/include/ -#GLIBLIB = -lglib - -# Should work with >= 1.2 -#GLIB_CFLAGS = $(shell glib-config glib --cflags) -#GLIBLIB = $(shell glib-config glib --libs) -#GLIB = -DUSE_GLIB $(GLIB_CFLAGS) - -#------------------------------------------------------------------------------ -# Video driver part -#------------------------------------------------------------------------------ - -# Uncomment the next for the normal X11 support. +# Use SIOD support +CCL = -DUSE_CCL2 +CCLLIB = -lm -ldl +# Video support VIDEO = -DUSE_X11 -VIDEOLIB = -lXext -lX11 -ldl - -# Uncomment the next to get the support for SDL. - -# Old SDL <1.0.0 -#SDL_CFLAGS = -#SDLLIB = -lSDL -ldl -lpthread - -# New SDL >=1.0.0 -SDL_CFLAGS = $(shell sdl-config --cflags) -#SDLLIB = $(shell sdl-config --static-libs) -SDLLIB = $(shell sdl-config --libs) - -# Without SDL Sound -#SDL = -DUSE_SDL $(SDL_CFLAGS) -# With SDL Sound -SDL = -DUSE_SDL -DUSE_SDLA $(SDL_CFLAGS) - -# Uncomment the next for the SDL X11/SVGALIB support. - -VIDEO = $(SDL) -VIDEOLIB = $(SDLLIB) -lXext -lX11 -lXxf86dga -lXxf86vm -lvga -lvgagl -ldl -lesd -lm - -# Choose next to get svgalib support. - -#VIDEO = -DUSE_SVGALIB -#VIDEOLIB = -lvga -lm -ldl - -# Uncomment the next for the win32/cygwin support. - -#VIDEO = -DUSE_WIN32 $(SDL) -#VIDEOLIB = $(SDLLIB) - -# Uncomment the next for the win32/mingw32 support. - -#VIDEO = -DUSE_WIN32 $(SDL) -#VIDEOLIB = $(SDLLIB) -lwsock32 -Wl,--stack,33554432 - -#------------------------------------------------------------------------------ -# Sound driver part -#------------------------------------------------------------------------------ - -# Comment next if you want to remove sound support. +VIDEOLIB = -lXext -lX11 -ldl +# Sound support DSOUND = -DWITH_SOUND -#------------------------------------------------------------------------------ - -# Choose which compress you like -# The win32 port didn't support BZ2LIB - -# None -#ZDEFS = -#ZLIBS = -# GZ compression -ZDEFS = -DUSE_ZLIB -ZLIBS = -lz -# BZ2 compression -#ZDEFS = -DUSE_BZ2LIB -#ZLIBS = -lbz2 -# GZ + BZ2 compression -#ZDEFS = -DUSE_ZLIB -DUSE_BZ2LIB -#ZLIBS = -lz -lbz2 - -#------------------------------------------------------------------------------ - -# May be required on some distributions for libpng and libz! -# extra linker flags and include directory -# -L/usr/lib +# Compression support +ZDEFS = -DUSE_ZLIB -DUSE_BZ2LIB +ZLIBS = -lz -lbz2 XLDFLAGS = -L/usr/X11R6/lib -L/usr/local/lib \ -L$(TOPDIR)/libpng-1.0.5 -L$(TOPDIR)/zlib-1.1.3 XIFLAGS = -I/usr/X11R6/include -I/usr/local/include \ -I$(TOPDIR)/libpng-1.0.5 -I$(TOPDIR)/zlib-1.1.3 -#------------------------------------------------------------------------------ - -# Uncomment next to profile -#PROFILE= -pg +##################################################################### +# Don't change anything below here unless you know what you're doing! -# Version VERSION= '-DVERSION="1.17pre1-build7"' +PROFILE= -############################################################################ -# below this, nothing should be changed! - -# Libraries needed to build tools TOOLLIBS=$(XLDFLAGS) -lpng -lz -lm $(THREADLIB) - -# Libraries needed to build freecraft CLONELIBS=$(XLDFLAGS) -lpng -lz -lm \ - $(THREADLIB) $(CCLLIB) $(GLIBLIB) $(VIDEOLIB) $(ZLIBS) - + $(THREADLIB) $(CCLLIB) $(VIDEOLIB) $(ZLIBS) DISTLIST=$(TOPDIR)/distlist TAGS=$(TOPDIR)/src/tags -# LINUX +# Linux +EXE= OUTFILE=$(TOPDIR)/freecraft ARCH=linux OE=o -EXE= -# WIN32 -#OUTFILE=$(TOPDIR)/freecraft$(EXE) -#ARCH=win32 -#OE=o -#EXE=.exe - -## architecture-dependant objects #ARCHOBJS=stdmman.$(OE) svgalib.$(OE) unix_lib.$(OE) bitm_lnx.$(OE) - -## include flags IFLAGS= -I$(TOPDIR)/src/include $(XIFLAGS) -## define flags -DEBUG= -DDEBUG #-DNEW_AI # -DFLAG_DEBUG -DFLAGS= $(THREAD) $(CCL) $(VERSION) $(GLIB) $(VIDEO) $(ZDEFS) $(DSOUND) \ - $(DEBUG) -DUNIT_ON_MAP # -DNEW_NAMES - -## choose optimise level -#CFLAGS=-g -O0 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS) -#CFLAGS=-g -O1 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS) -#CFLAGS=-g -O2 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS) -CFLAGS=-g -O3 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS) -#CFLAGS=-g -O3 $(PROFILE) -pipe -Wall $(IFLAGS) $(DFLAGS) -#CFLAGS=-g -O6 -pipe -fconserve-space -fexpensive-optimizations -ffast-math $(IFLAGS) $(DFLAGS) -#-- Production -#CFLAGS=-O6 -pipe -fomit-frame-pointer -fconserve-space -fexpensive-optimizations -ffast-math $(IFLAGS) $(DFLAGS) -CFLAGS=-O6 -pipe -fomit-frame-pointer -fconserve-space -fexpensive-optimizations -ffast-math $(IFLAGS) $(DFLAGS) -static - -CC=gcc -RM=rm -f -MAKE=make - -## JOHNS: my ctags didn't support -#CTAGSFLAGS=-i defmpstuvFS -a -f +DFLAGS= $(THREAD) $(CCL) $(VERSION) \ + $(VIDEO) $(ZDEFS) $(DSOUND) \ + $(DEBUG) +CFLAGS=-g $(IFLAGS) $(DFLAGS) -DSLOW_INPUT -DUNIT_ON_MAP CTAGSFLAGS=-i defptvS -a -f -# -# Locks versions with symbolic name -# +# Locks versions with a symbolic name LOCKVER= rcs -q -n$(NAME) %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ @ar cru $(TOPDIR)/src/libclone.a $@ -#------------ -# Source code documentation -# +# Source code documentation DOXYGEN= doxygen DOCIFY= docify DOCPP= doc++ -# Still didn't work -#DOCIFY= /root/doc++-3.4.2/src/docify -#DOCPP= /root/doc++-3.4.2/src/doc++ %.doc: %.c @$(TOPDIR)/tools/aledoc $< | $(DOCIFY) > $*-c.doc 2>/dev/null - %.doc: %.h @$(TOPDIR)/tools/aledoc $< | $(DOCIFY) > $*-h.doc 2>/dev/null diff --git a/src/action/Makefile b/src/action/Makefile index 228793b98..b30220756 100644 --- a/src/action/Makefile +++ b/src/action/Makefile @@ -1,6 +1,6 @@ ## ___________ _________ _____ __ ## \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ -## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ ## | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | ## \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| ## \/ \/ \/ \/ \/ @@ -27,6 +27,7 @@ OBJS = command.$(OE) actions.$(OE) action_die.$(OE) action_patrol.$(OE) \ action_board.$(OE) action_unload.$(OE) action_repair.$(OE) \ action_harvest.$(OE) action_minegold.$(OE) action_hauloil.$(OE) \ action_returngoods.$(OE) action_research.$(OE) action_demolish.$(OE) \ - action_build.$(OE) action_train.$(OE) action_upgradeto.$(OE) + action_build.$(OE) action_train.$(OE) action_upgradeto.$(OE) \ + action_spellcast.$(OE) include $(TOPDIR)/Common.mk diff --git a/src/action/action_attack.cpp b/src/action/action_attack.cpp index c0741c76a..5c0757887 100644 --- a/src/action/action_attack.cpp +++ b/src/action/action_attack.cpp @@ -73,6 +73,7 @@ local void DoActionAttackGeneric(Unit* unit,const Animation* attack) if( flags&AnimationMissile ) { // time to fire projectil FireMissile(unit); + unit->Invisible = 0; // unit is invisible untill attacks } } diff --git a/src/action/action_demolish.cpp b/src/action/action_demolish.cpp index f4a68d271..392c6f694 100644 --- a/src/action/action_demolish.cpp +++ b/src/action/action_demolish.cpp @@ -130,7 +130,8 @@ global void HandleActionDemolish(Unit* unit) n=SelectUnits(x-2,y-2, x+2, y+2,table); // FIXME: Don't hit flying units! for( i=0; i<n; ++i ) { - HitUnit(table[i],DEMOLISH_DAMAGE); + if ( table[i]->Type->LandUnit ) + HitUnit(table[i],DEMOLISH_DAMAGE); } // diff --git a/src/action/action_die.cpp b/src/action/action_die.cpp index 86c8e4047..a0fec658b 100644 --- a/src/action/action_die.cpp +++ b/src/action/action_die.cpp @@ -46,6 +46,14 @@ */ global int HandleActionDie(Unit* unit) { + if ( unit->Revealer ) + { + unit->HP--; + if( unit->HP == 0 ) + ReleaseUnit(unit); + return 0; + } + // // Show death animation // diff --git a/src/action/actions.cpp b/src/action/actions.cpp index d1fdf115c..14097367c 100644 --- a/src/action/actions.cpp +++ b/src/action/actions.cpp @@ -94,6 +94,10 @@ global int UnitShowAnimation(Unit* unit,const Animation* animation) */ local void HandleUnitAction(Unit* unit) { + if ( unit->Revealer ) + { + unit->Command.Action = UnitActionDie; + } // // If current action is breakable proceed with next one. // @@ -237,6 +241,10 @@ local void HandleUnitAction(Unit* unit) HandleActionDemolish(unit); break; + case UnitActionSpellCast: + HandleActionSpellCast(unit); + break; + default: DebugLevel1Fn("Unknown action %d\n",unit->Command.Action); break; diff --git a/src/action/command.cpp b/src/action/command.cpp index f48e8213a..1ec131893 100644 --- a/src/action/command.cpp +++ b/src/action/command.cpp @@ -33,6 +33,7 @@ #include "map.h" #include "upgrade.h" #include "pathfinder.h" +#include "spells.h" /*---------------------------------------------------------------------------- -- Functions @@ -829,4 +830,66 @@ global void CommandDemolish(Unit* unit,int x,int y,Unit* dest,int flush) unit->SavedCommand.Action=UnitActionStill; // clear saved action } +/** +** Demolish at position +** +** @param unit pointer to unit. +** @param x X map position to spell cast on. +** @param y Y map position to spell cast on. +** @param dest Spell cast on unit (if exist). +** @param spellid Spell type id. +** @param flush if true, flush command queue. +*/ +global void CommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid,int flush) +{ + Command* command; + const SpellType* spell; + + IfDebug( + if( x<0 || y<0 || x>=TheMap.Width || y>=TheMap.Height ) { + DebugLevel0("Internal movement error\n"); + return; + } + if( unit->Type->Vanishes ) { + DebugLevel0("Internal error\n"); + abort(); + } + ); + + DebugLevel3(__FUNCTION__": %Zd spell-casts on %Zd\n" + ,UnitNumber(unit),dest ? UnitNumber(dest) : 0); + + if( unit->Type->Building ) { + // FIXME: should find a better way for pending commands. + command=&unit->PendCommand; + } else if( !(command=GetNextCommand(unit,flush)) ) { + return; + } + + spell = SpellTypeById( spellid ); + + command->Action=UnitActionSpellCast; + ResetPath(*command); + + if (dest) + dest->Refs++; + + command->Data.Move.Goal=dest; + command->Data.Move.Range=spell->Range; + command->Data.Move.SX=unit->X; + command->Data.Move.SY=unit->Y; + command->Data.Move.DX=x; + command->Data.Move.DY=y; + command->Data.Move.SpellId = spellid; + + /* + command->Data.Spell.Goal=dest; + command->Data.Spell.Range=spell->Range; + command->Data.Spell.DX=x; + command->Data.Spell.DY=y; + */ + + unit->SavedCommand.Action=UnitActionStill; // clear saved action +} + //@} diff --git a/src/include/actions.h b/src/include/actions.h index 9b356c72c..fc42bb51d 100644 --- a/src/include/actions.h +++ b/src/include/actions.h @@ -88,6 +88,8 @@ extern void CommandCancelResearch(Unit* unit); //extern void CommandUpgradeUnit(Unit* unit,int what,int flush); /// Prepare command demolish extern void CommandDemolish(Unit* unit,int x,int y,Unit* dest,int flush); + /// Prepare command spellcast +extern void CommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid, int flush); /*---------------------------------------------------------------------------- -- Actions: in action_<name>.c @@ -135,6 +137,8 @@ extern void HandleActionUpgrade(Unit* unit); extern void HandleActionResearch(Unit* unit); /// Handle command demolish extern void HandleActionDemolish(Unit* unit); + /// Handle command spellcast +extern void HandleActionSpellCast(Unit* unit); /*---------------------------------------------------------------------------- -- Actions: actions.c diff --git a/src/include/cursor.h b/src/include/cursor.h index a6163c596..f86d17fbb 100644 --- a/src/include/cursor.h +++ b/src/include/cursor.h @@ -82,6 +82,7 @@ extern CursorType Cursors[CursorMax]; /// cursor types description extern enum CursorState_e CursorState; /// cursor state extern int CursorAction; /// action for selection +extern int CursorValue; /// value for CursorAction (spell type f.e.) extern UnitType* CursorBuilding; /// building cursor extern CursorType* GameCursor; /// cursor type diff --git a/src/include/interface.h b/src/include/interface.h index 3d3b74b81..2faddf600 100644 --- a/src/include/interface.h +++ b/src/include/interface.h @@ -47,7 +47,7 @@ enum _button_cmd_ { B_AttackGround, /// order attack ground B_Return, /// order return goods B_Demolish, /// order demolish/explode - B_Magic, /// order cast spell + B_SpellCast, /// order cast spell B_Research, /// order reseach B_UpgradeTo, /// order upgrade B_Unload, /// order unload unit diff --git a/src/include/missile.h b/src/include/missile.h index a601dd494..4ec1cbf10 100644 --- a/src/include/missile.h +++ b/src/include/missile.h @@ -69,7 +69,7 @@ struct _missile_type_ { }; /// how many missile type are maximal supported -#define MissileTypeMax 0x1E +#define MissileTypeMax 0x22 /// mark a free missile slot #define MissileFree (MissileType*)0 @@ -96,11 +96,18 @@ typedef struct _missile_ { UnitStats* SourceStats; /// stats of unit that fires Player* SourcePlayer; /// player of unit that fires + Unit* TargetUnit; /// target unit, used for spells + + int Damage; /// direct damage that missile applies + int D; /// for point to point missiles int Dx; /// delta x int Dy; /// delta y int Xstep; /// X step int Ystep; /// Y step + + int TTL; /// time to live (ticks) used for spells + int (*Controller)( void* this_missile ); /// used to controll spells } Missile; /*---------------------------------------------------------------------------- diff --git a/src/include/network.h b/src/include/network.h index 1515fa80e..cfdbd4a14 100644 --- a/src/include/network.h +++ b/src/include/network.h @@ -105,7 +105,8 @@ extern void SendCommandResearch(Unit* unit,Upgrade* what,int flush); extern void SendCommandCancelResearch(Unit* unit); /// Send demolish command extern void SendCommandDemolish(Unit* unit,int x,int y,Unit* dest,int flush); - + /// Send spell cast command +extern void SendCommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid,int flush); //@} #endif // !__NETWORK_H__ diff --git a/src/include/spells.h b/src/include/spells.h new file mode 100644 index 000000000..ce48411c6 --- /dev/null +++ b/src/include/spells.h @@ -0,0 +1,112 @@ +// ___________ _________ _____ __ +// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ +// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | +// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| +// \/ \/ \/ \/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// FreeCraft - A free fantasy real time strategy game engine +// +/**@name spells.h - The Spells. */ +/* +** (c) Copyright 1999,2000 by Vladi Belperchinov-Shabanski +** +** $Id$ +*/ + +#ifndef __SPELLS_H__ +#define __SPELLS_H__ + +//@{ + +/*---------------------------------------------------------------------------- +-- Includes +----------------------------------------------------------------------------*/ +#include <stdio.h> + +#include "freecraft.h" +#include "sound_id.h" +#include "sound.h" +#include "unittype.h" +#include "unit.h" + +/*---------------------------------------------------------------------------- +-- Definitons +----------------------------------------------------------------------------*/ + + +typedef enum _spell_action_type_ { + SpellActionNone, +// ---human paladins--- + SpellActionHolyVision, + SpellActionHealing, + SpellActionExorcism, +// ---human mages--- + SpellActionFireball, + SpellActionSlow, + SpellActionFlameShield, + SpellActionInvisibility, + SpellActionPolymorph, + SpellActionBlizzard, +// ---orc ogres--- + SpellActionEyeOfKilrogg, + SpellActionBloodlust, + SpellActionRunes, +// ---orc death knights--- + SpellActionDeathCoil, + SpellActionHaste, + SpellActionRaiseDead, + SpellActionWhirlwind, + SpellActionUnholyArmor, + SpellActionDeathAndDecay +} SpellActionType; + +typedef struct _spell_type_ { + + int Id; /// index in the table (set by InitSpells()) + char Ident[64]; /// spell name (ident) + + int Range; /// spell range + int ManaCost; /// required mana for each cast + int TTL; /// time to live (ticks) + + SpellActionType Action; /// SpellAction* + + char SoundIdent[64]; /// sound string id + SoundId SoundId; /// sound id + +} SpellType; + +/*---------------------------------------------------------------------------- +-- Variables +----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- +-- Functions +----------------------------------------------------------------------------*/ + + /// init spell tables +extern global void InitSpells(); + + /// done spell tables +extern global void DoneSpells(); + + /// return spell id by ident string +extern global int SpellIdByIdent( const char* Ident ); + + /// return spell type by ident string +extern global const SpellType* SpellTypeByIdent( const char* Ident ); + + /// return spell type by ident string +extern global const SpellType* SpellTypeById( int Id ); + +/* /// returns != 0 if spell can be casted by this unit, enough mana? +extern global int CanCastSpell( Unit* unit, int SpellId ); +*/ + /// fire spell on target unit or place at x,y +extern global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y ); + +//@} + +#endif // !__BUTTON_H__ diff --git a/src/include/unit.h b/src/include/unit.h index df18c135f..c7290a5d8 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -1,6 +1,6 @@ // ___________ _________ _____ __ // \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ -// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ // | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | // \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| // \/ \/ \/ \/ \/ @@ -62,6 +62,8 @@ enum _unit_action_ { UnitActionAttackGround, /// unit attacks ground UnitActionDie, /// unit dies + UnitActionSpellCast, /// unit casts spell + UnitActionTrain, /// building is training UnitActionUpgradeTo, /// building is upgrading itself // UnitActionUpgrade, /// building is researching upgrade @@ -130,6 +132,7 @@ struct _command_ { unsigned SY; /// Source unsigned DX; unsigned DY; /// Destination + int SpellId; /// spell type id } Move; /// move: struct { unsigned Fast : 1; /// Can fast move @@ -152,7 +155,7 @@ struct _command_ { struct { unsigned Ticks; /// Ticks to complete unsigned Count; /// Units in training queue - // FIXME: cade: later we should train more units or automatic + // FIXME: vladi: later we should train more units or automatic #define MAX_UNIT_TRAIN 6 UnitType* What[MAX_UNIT_TRAIN]; /// Unit trained } Train; /// train: @@ -230,7 +233,7 @@ struct _unit_ { UnitType* Type; /// pointer to unit type (peon,...) Player* Player; /// owner of this unit UnitStats* Stats; /// current unit stats - + // DISPLAY: char IX; char IY; /// image displacement to map position @@ -252,10 +255,11 @@ struct _unit_ { unsigned HP; /// hit points unsigned Bloodlust; /// ticks bloodlust - unsigned Haste; /// ticks haste - unsigned Slow; /// ticks slow + unsigned Haste; /// ticks haste (disables slow) + unsigned Slow; /// ticks slow (disables haste) unsigned Invisible; /// ticks invisible - unsigned Shield; /// ticks shield + unsigned FlameShield; /// ticks flame shield + unsigned UnholyArmor; /// ticks unholy armor unsigned GroupId; /// unit belongs to this group id @@ -273,6 +277,10 @@ struct _unit_ { ** ,used for fancy buildings */ unsigned Rs : 8; + unsigned Revealer; // hack -- `revealer' is unit that + // has to keep FOW revealed for some + // time, this unit cannot be used in + // usual manner #define MAX_UNITS_ONBOARD 6 /// max number of units in transporter // FIXME: use the new next pointer diff --git a/src/map/map_fog.cpp b/src/map/map_fog.cpp index b60394a2e..b4d4d1558 100644 --- a/src/map/map_fog.cpp +++ b/src/map/map_fog.cpp @@ -358,11 +358,13 @@ global void MapUpdateVisible(void) #ifdef NEW_FOW MapMarkSight(unit->Player,unit->X+unit->Type->TileWidth/2 ,unit->Y+unit->Type->TileHeight/2 - ,unit->Stats->SightRange); + ,unit->Stats->SightRange*(unit->Revealer == 0) + + 12*(unit->Revealer != 0)); #else MapMarkSight(unit->X+unit->Type->TileWidth/2 ,unit->Y+unit->Type->TileHeight/2 - ,unit->Stats->SightRange); + ,unit->Stats->SightRange*(unit->Revealer == 0) + + 12*(unit->Revealer != 0)); #endif } } diff --git a/src/network/network.cpp b/src/network/network.cpp index 8b72fecd1..4daf48736 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -113,6 +113,7 @@ enum _message_type_ { MessageCommandResearch, /// unit command research MessageCommandCancelResearch, /// unit command cancel research MessageCommandDemolish, /// unit command demolish + MessageCommandSpellCast /// unit command spell cast }; /** @@ -710,6 +711,30 @@ global void SendCommandDemolish(Unit* unit,int x,int y,Unit* attack,int flush) } } +/** +** Send command: Unit spell cast on position/unit. +** +** @param unit pointer to unit. +** @param x X map tile position where to cast spell. +** @param y Y map tile position where to cast spell. +** @param attack Cast spell on unit (if exist). +** @param spellid Spell type id. +** @param flush Flag flush all pending commands. +*/ +global void SendCommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid,int flush) +{ + CommandLog("spell-cast",unit,flush,1,x,y,dest,NULL); //FIXME: vladi: spellid? + if( NetworkFildes==-1 ) { + CommandSpellCast(unit,x,y,dest,spellid,flush); + } else { + // FIXME: WARNING: vladi: this is weird, we should have and + // integer argument to all this or just few bytes buffer or + // something... I'll pass spell id's as pointers until... + UnitType* ut = (UnitType*)spellid; + NetworkSendCommand(MessageCommandSpellCast,unit,x,y,dest,ut,flush); + } +} + //@} //---------------------------------------------------------------------------- @@ -1718,6 +1743,12 @@ local void ParseNetworkCommand(const NetworkCommandQueue* ncq) } CommandDemolish(unit,x,y,dest,status); break; + case MessageCommandSpellCast: + // FIXME: WARNING: vladi: currently network protocol + // cannot carry required information for spell cast + // it is required to handle dest unit and aditional + // integer value! + break; } } diff --git a/src/stratagus/Makefile b/src/stratagus/Makefile index e84763a60..ff4382b7f 100644 --- a/src/stratagus/Makefile +++ b/src/stratagus/Makefile @@ -24,6 +24,7 @@ MODULE = clone OBJS = clone.$(OE) unit.$(OE) unit_draw.$(OE) unitcache.$(OE) \ unit_find.$(OE) missile.$(OE) construct.$(OE)\ player.$(OE) pud.$(OE) ccl.$(OE) interface.$(OE) iolib.$(OE) \ - mainloop.$(OE) goal.$(OE) selection.$(OE) groups.$(OE) + mainloop.$(OE) goal.$(OE) selection.$(OE) groups.$(OE) \ + spells.$(OE) include $(TOPDIR)/Common.mk diff --git a/src/stratagus/interface.cpp b/src/stratagus/interface.cpp index c7d9cd9c8..dac0be544 100644 --- a/src/stratagus/interface.cpp +++ b/src/stratagus/interface.cpp @@ -318,7 +318,27 @@ local int InputKey(int key) SpeedResearch=1; /// speed factor for researching SetMessage( "NORMAL DEBUG SPEED" ); } - + if (strcmp(Input, "make it so") == 0) + { + SpeedMine=10; /// speed factor for mine gold + SpeedGold=10; /// speed factor for getting gold + SpeedChop=10; /// speed factor for chop + SpeedWood=10; /// speed factor for getting wood + SpeedHaul=10; /// speed factor for haul oil + SpeedOil=10; /// speed factor for getting oil + SpeedBuild=10; /// speed factor for building + SpeedTrain=10; /// speed factor for training + SpeedUpgrade=10; /// speed factor for upgrading + SpeedResearch=10; /// speed factor for researching + ThisPlayer->Resources[GoldCost] += 32000; + ThisPlayer->Resources[WoodCost] += 32000; + ThisPlayer->Resources[OilCost] += 32000; + ThisPlayer->Resources[OreCost] += 32000; + ThisPlayer->Resources[StoneCost]+= 32000; + ThisPlayer->Resources[CoalCost] += 32000; + MustRedraw|=RedrawResources; + SetMessage( "SO!" ); + } else // FIXME: only to selected players NetworkChatMessage(Input); #if defined(USE_CCL) || defined(USE_CCL2) diff --git a/src/stratagus/oldmissile.cpp b/src/stratagus/oldmissile.cpp index 8b38fb8ed..55ae18372 100644 --- a/src/stratagus/oldmissile.cpp +++ b/src/stratagus/oldmissile.cpp @@ -95,6 +95,10 @@ ** Missile don't move, than checks the source unit for HP. */ #define MissileClassFire 12 +/** +** Missile is controlled completely by Controller() function. (custom) +*/ +#define MissileClassCustom 13 /*---------------------------------------------------------------------------- -- Variables @@ -135,6 +139,9 @@ local const char* MissileTypeWcNames[] = { "missile-cannon-tower-explosion", "missile-daemon-fire", "missile-green-cross", + "missile-blizzard-hit", + "missile-death-coil", + "missile-custom", "missile-none", }; @@ -178,7 +185,7 @@ global MissileType MissileTypes[MissileTypeMax] = { "fireball.png", 32,32, { "fireball hit" }, - MissileClassFireball, + MissileClassPointToPoint, 1, }, { MissileTypeType, @@ -194,15 +201,16 @@ global MissileType MissileTypes[MissileTypeMax] = { "blizzard.png", 32,32, { NULL }, - MissileClassBlizzard, + MissileClassBlizzard, 1, + "missile-blizzard-hit", NULL }, { MissileTypeType, "missile-death-and-decay", "death and decay.png", 32,32, { NULL }, - MissileClassDeathDecay, + MissileClassStayWithDelay, 1, }, { MissileTypeType, @@ -227,7 +235,7 @@ global MissileType MissileTypes[MissileTypeMax] = { "heal effect.png", 48,48, { NULL }, - MissileClassPointToPoint, + MissileClassStayWithDelay, 1, }, { MissileTypeType, @@ -244,7 +252,7 @@ global MissileType MissileTypes[MissileTypeMax] = { 16,16, { NULL }, MissileClassStayWithDelay, - 1, + 5, }, { MissileTypeType, "missile-whirlwind", @@ -320,7 +328,7 @@ global MissileType MissileTypes[MissileTypeMax] = { 48,48, { NULL }, MissileClassFire, - 8, + 8, }, { MissileTypeType, "missile-impact", @@ -395,6 +403,38 @@ global MissileType MissileTypes[MissileTypeMax] = { MissileClassNone, 1, }, +{ MissileTypeType, + "missile-blizzard-hit", + "blizzard.png", + 32,32, + { NULL }, + MissileClassStayWithDelay, + 1, + }, +{ MissileTypeType, + "missile-death-coil", + "touch of death.png", + 32,32, + { NULL }, + MissileClassPointToPoint, + 1, + }, +{ MissileTypeType, + "missile-custom", + NULL, + 32,32, + { NULL }, + MissileClassCustom, + 1, + }, +{ MissileTypeType, + "missile-none", + NULL, + 32,32, + { NULL }, + MissileClassNone, + 1, + }, }; /* @@ -409,7 +449,7 @@ local int NumMissiles; /// currently used missiles local Missile Missiles[MAX_MISSILES]; /// all missiles on map /// lookup table for missile names -local hashtable(MissileType*,61) MissileHash; +local hashtable(MissileType*,65) MissileHash; /*---------------------------------------------------------------------------- -- Functions @@ -531,6 +571,11 @@ found: missile->SourceStats=NULL; missile->SourcePlayer=NULL; + missile->Damage = 0; + missile->TargetUnit = NULL; + missile->TTL = -1; + missile->Controller = NULL; + return missile; } @@ -762,6 +807,9 @@ global void DrawMissiles(void) if( missile->Type==MissileFree ) { continue; } + if( missile->Type->Class == MissileClassCustom ) { + continue; // custom missiles are handled by Controller() only + } // Draw only visibile missiles if (MissileVisible(missile)) { x=missile->X-MapX*TileSizeX+TheUI.MapX; @@ -830,7 +878,15 @@ local int PointToPointMissile(Missile* missile) } // FIXME: could be better written - MissileNewHeadingFromXY(missile,dx*xstep,dy*ystep); + if( missile->Type->Class == MissileClassWhirlwind ) + { + // must not call MissileNewHeading nor frame change + } + else + if( missile->Type->Class == MissileClassBlizzard ) + missile->Frame = 0; + else + MissileNewHeadingFromXY(missile,dx*xstep,dy*ystep); if( dy==0 ) { // horizontal line if( dx==0 ) { @@ -943,7 +999,9 @@ global void MissileHit(const Missile* missile) x=missile->X+missile->Type->Width/2; y=missile->Y+missile->Type->Height/2; if( missile->Type->ImpactMissile ) { - MakeMissile(missile->Type->ImpactMissile,x,y,0,0); + Missile* mis = MakeMissile(missile->Type->ImpactMissile,x,y,0,0); + mis->Damage = missile->Damage; // direct damage, spells mostly + mis->SourceUnit = missile->SourceUnit; } if( !missile->SourceType ) { // no target return; @@ -961,19 +1019,27 @@ global void MissileHit(const Missile* missile) DebugLevel3Fn("Missile on wall?\n"); // FIXME: don't use UnitTypeByIdent here, this is slow! if( HumanWallOnMap(x,y) ) { - HitWall(x,y,CalculateDamageStats(missile->SourceStats, - UnitTypeByIdent("unit-human-wall")->Stats)); + if ( missile->Damage ) + HitWall(x,y,missile->Damage); // direct damage, spells mostly + else + HitWall(x,y,CalculateDamageStats(missile->SourceStats, + UnitTypeByIdent("unit-human-wall")->Stats)); } else { - HitWall(x,y,CalculateDamageStats(missile->SourceStats, - UnitTypeByIdent("unit-orc-wall")->Stats)); + if ( missile->Damage ) + HitWall(x,y,missile->Damage); // direct damage, spells mostly + else + HitWall(x,y,CalculateDamageStats(missile->SourceStats, + UnitTypeByIdent("unit-orc-wall")->Stats)); } return; } DebugLevel3Fn("Oops nothing to hit (%d,%d)?\n",x,y); return; } - - HitUnit(goal,CalculateDamage(missile->SourceStats,goal)); + if ( missile->Damage ) + HitUnit(goal,missile->Damage); // direct damage, spells mostly + else + HitUnit(goal,CalculateDamage(missile->SourceStats,goal)); } /** @@ -992,6 +1058,25 @@ global void MissileActions(void) if( missile->Wait-- ) { continue; } + + if ( missile->TTL != -1 ) { + missile->TTL--; // overall time to live if specified + } + + if ( missile->Controller ) { + missile->Controller( missile ); + } + + if ( missile->TTL == 0 ) { + missile->Type=MissileFree; + continue; + } + + if ( missile->Type->Class == MissileClassCustom ) { + missile->Wait=missile->Type->Speed; + continue; // custom missiles are handled by Controller() only + } + if (MissileVisible(missile)) { // check before movement MustRedraw|=RedrawMap; @@ -1088,6 +1173,31 @@ global void MissileActions(void) } break; + case MissileClassBlizzard: + missile->Wait=missile->Type->Speed; + if( PointToPointMissile(missile) ) { + // + // Animate hit + // + missile->Frame+=4; // FIXME: frames pro row + if( (missile->Frame&127) + >=VideoGraphicFrames(missile->Type->Sprite) ) { + MissileHit(missile); + missile->Type=MissileFree; + } + } + break; + case MissileClassWhirlwind: + missile->Wait=missile->Type->Speed; + missile->Frame++; + if ( missile->Frame > 3 ) + missile->Frame = 0; + //NOTE: vladi: whirlwind moves slowly, i.e. it stays + // 5 ticks at the same pixels... + if ( missile->TTL < 1 || missile->TTL % 10 == 0 ) + PointToPointMissile(missile); + break; + case MissileClassStayWithDelay: missile->Wait=missile->Type->Speed; if( ++missile->Frame diff --git a/src/stratagus/selection.cpp b/src/stratagus/selection.cpp index 13f509b16..83869e472 100644 --- a/src/stratagus/selection.cpp +++ b/src/stratagus/selection.cpp @@ -89,6 +89,11 @@ global void ChangeSelectedUnits(Unit** units,int count) */ global int SelectUnit(Unit* unit) { + if ( unit->Revealer ) + { + return 0; // Revealers cannot be selected + } + if( NumSelected == MaxSelectable ) { return 0; } diff --git a/src/stratagus/spells.cpp b/src/stratagus/spells.cpp new file mode 100644 index 000000000..640c2672f --- /dev/null +++ b/src/stratagus/spells.cpp @@ -0,0 +1,793 @@ +// ___________ _________ _____ __ +// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ +// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | +// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| +// \/ \/ \/ \/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// FreeCraft - A free fantasy real time strategy game engine +// +/**@name action_spellcast.c - The spell cast action. */ +/* +** (c) Copyright 1998-2000 by Vladi Belperchinov-Shabanski +** +** $Id$ +*/ +/* +** And when we cast our final spell +** And we meet in our dreams +** A place that no one else can go +** Don't ever let your love die +** Don't ever go breaking this spell +*/ + +//@{ + +/*---------------------------------------------------------------------------- +-- Notes +----------------------------------------------------------------------------*/ + + + +/*---------------------------------------------------------------------------- +-- Includes +----------------------------------------------------------------------------*/ +#include "spells.h" +#include "sound.h" +#include "missile.h" +#include "map.h" +#include "ui.h" + +/*---------------------------------------------------------------------------- +-- Definitons +----------------------------------------------------------------------------*/ + +#define FIREBALL_DAMAGE 20 +#define WHIRLWIND_DAMAGE1 4 // the center of the whirlwind +#define WHIRLWIND_DAMAGE2 1 // the periphery of the whirlwind +#define BLIZZARD_DAMAGE 10 +#define DEATHANDDECAY_DAMAGE 10 +#define RUNE_DAMAGE 50 + +/*---------------------------------------------------------------------------- +-- Variables +----------------------------------------------------------------------------*/ + +/* + NOTE: vladi: + + The point to have variable unosrted list of spell types and + dynamic id's and in the same time -- SpellAction id's is that + spell actions are hardcoded and cannot be changed at all. + On the other hand we can have different spell types as with + different range, cost and time to live (possibly and other + parameters as extensions) +*/ + +global SpellType SpellTypeTable[] = { + +//TTL's below are in ticks: approx: 500=13sec, 1000=25sec, 2000=50sec + +//id, ident, range, mana, ttl, spell action, sound ident, sound id +// ---human paladins--- +{ 0, "spell-holy-vision", 1024, 70, -1, SpellActionHolyVision , "holy vision", NULL }, +{ 0, "spell-healing", 4, 6, -1, SpellActionHealing , "healing", NULL }, +{ 0, "spell-exorcism", 10, 4, -1, SpellActionExorcism , "exorcism", NULL }, +// ---human mages--- ---human mages--- +{ 0, "spell-fireball", 8, 100, 999, SpellActionFireball , "fireball", NULL }, +{ 0, "spell-slow", 10, 50,1000, SpellActionSlow , "slow", NULL }, +{ 0, "spell-flame-shield", 6, 80, 600, SpellActionFlameShield , "flame shield", NULL }, +{ 0, "spell-invisibility", 6, 200,2000, SpellActionInvisibility , "invisibility", NULL }, +{ 0, "spell-polymorph", 10, 200, -1, SpellActionPolymorph , "polymorph", NULL }, +{ 0, "spell-blizzard", 12, 5, -1, SpellActionBlizzard , "blizzard", NULL }, +// ---orc ogres--- ---orc ogres--- +{ 0, "spell-eye-of-kilrogg",1024, 70, -1, SpellActionEyeOfKilrogg , "eye of kilrogg", NULL }, +{ 0, "spell-bloodlust", 6, 50,1000, SpellActionBloodlust , "bloodlust", NULL }, +{ 0, "spell-runes", 10, 50,2000, SpellActionRunes , "runes", NULL }, +// ---orc death knights--- ---orc death knights- +{ 0, "spell-death-coil", 10, 100, -1, SpellActionDeathCoil , "death coil", NULL }, +{ 0, "spell-haste", 6, 50,1000, SpellActionHaste , "haste", NULL }, +{ 0, "spell-raise-dead", 6, 50, -1, SpellActionRaiseDead , "raise dead", NULL }, +{ 0, "spell-whirlwind", 12, 100, 800, SpellActionWhirlwind , "whirlwind", NULL }, +{ 0, "spell-unholy-armor", 6, 100, 500, SpellActionUnholyArmor , "unholy armor", NULL }, +{ 0, "spell-death-and-decay", 12, 5, -1, SpellActionDeathAndDecay, "death and decay",NULL }, +// ---eot marker--- ---eot marker--- +{-1, "", 1, 1, -1, SpellActionNone , "", } +}; + +local int SpellTypeCount; + +MissileType* missile_healing = NULL; +MissileType* missile_spell = NULL; +MissileType* missile_exorcism = NULL; +MissileType* missile_explosion = NULL; +MissileType* missile_rune = NULL; + +/*---------------------------------------------------------------------------- +-- Functions (Spells Controllers/Callbacks) +----------------------------------------------------------------------------*/ + +/* +** Missile controllers should return 0 to continue +** or 1 to cancel the spell/missile +*/ + +/* +** Fireball controller +*/ +global int SpellFireballController( void* missile ) +{ + Unit* table[MAX_UNITS]; + int i; + int n; + + Missile* mis = (Missile*)missile; + + //NOTE: vladi: TTL is used as counter for explosions + // first 6 counts there are no explosions, then on each second + // and at the target destination + if ( (mis->TTL < 993 && mis->TTL % 2 == 0) || (mis->X == mis->DX && mis->Y == mis->DY) ) // approx. + { + //+TileSize/2 to align gfx to baseline + int x = mis->X + TileSizeX/2; + int y = mis->Y + TileSizeY/2; + MakeMissile( missile_explosion, x, y, x, y ); + + x = x / TileSizeX; + y = y / TileSizeY; + + //Effect of the explosion on units. + //NOTE: vladi: this is slightly different than original + // now it hits all units in range 1 + n = SelectUnits(x-1,y-1, x+1, y+1,table); + for( i=0; i<n; ++i ) + HitUnit(table[i],FIREBALL_DAMAGE); + } + return 0; +}; + +/* +** Death-Coil controller +*/ +global int SpellDeathCoilController( void* missile ) +{ + Unit* table[MAX_UNITS]; + int i; + int n; + + Missile* mis = (Missile*)missile; + mis->SourceUnit->Refs--; + if ( mis->TargetUnit ) + mis->TargetUnit->Refs--; + if ( mis->X == mis->DX && mis->Y == mis->DY ) + { // missile has reached target unit/spot + if ( !mis->SourceUnit->Destroyed ) + { // source unit still exists + if ( mis->TargetUnit && !mis->TargetUnit->Destroyed ) + { // target unit still exists + int hp = mis->TargetUnit->HP; + hp -= 50; + mis->SourceUnit->HP += 50; + if ( hp <= 0 ) + { + mis->TargetUnit->HP = 0; + DestroyUnit( mis->TargetUnit ); + } + else + mis->TargetUnit->HP = hp; + if ( mis->SourceUnit->HP > mis->SourceUnit->Stats->HitPoints ) + mis->SourceUnit->HP = mis->SourceUnit->Stats->HitPoints; + } + else + { // no target unit -- try enemies in range 5x5 + int ec = 0; // enemy count + int x = mis->DX / TileSizeX; + int y = mis->DY / TileSizeY; + n = SelectUnits(x-2,y-2, x+2, y+2,table); + if ( n > 0 ) + { + // calculate organic enemy count + for( i=0; i<n; ++i ) + ec += ( IsEnemy(mis->SourceUnit->Player,table[i]) + && table[i]->Type->Organic != 0); + if ( ec > 0 ) + { // yes organic enemies found + for( i=0; i<n; ++i ) + if ( IsEnemy(mis->SourceUnit->Player,table[i]) + && table[i]->Type->Organic != 0 ) + { + // disperse dabage between them + int hp = table[i]->HP; + hp -= 50/ec; //NOTE: 1 is the minimal damage + if ( hp <= 0 ) + { + table[i]->HP = 0; + DestroyUnit( table[i] ); // too much damage + } + else + table[i]->HP = hp; + } + mis->SourceUnit->HP += 50; + if ( mis->SourceUnit->HP > mis->SourceUnit->Stats->HitPoints ) + mis->SourceUnit->HP = mis->SourceUnit->Stats->HitPoints; + } + } + } + } + } + return 0; +} + +/* +** Whirlwind controller +*/ +/* + FIXME: vladi: whirlwind is particulary bad! :) + we need slow smooth missile movement that we don't + have yet... should be fixed later +*/ +global int SpellWhirlwindController( void* missile ) +{ + Unit* table[MAX_UNITS]; + int i; + int n; + int x; + int y; + + Missile* mis = (Missile*)missile; + x = mis->X / TileSizeX; + y = mis->Y / TileSizeY; + + n = SelectUnitsOnTile( x, y, table); + for( i=0; i<n; ++i ) { + HitUnit(table[i],WHIRLWIND_DAMAGE1); + } + n = SelectUnits( x - 1, y - 1, x + 1, y + 1, table); + for( i=0; i<n; ++i ) { + HitUnit(table[i],WHIRLWIND_DAMAGE2); + } + //printf( "Whirlwind: %d, %d, TTL: %d\n", mis->X, mis->Y, mis->TTL ); + if ( mis->TTL % 100 == 0 ) // changes direction every 3 seconds (approx.) + { // missile has reached target unit/spot + int nx, ny; + do + { + // find new destination in the map + nx = x + rand() % 5 - 2; + ny = y + rand() % 5 - 2; + } + while( nx < 0 && ny < 0 && nx >= TheMap.Width && ny >= TheMap.Height ); + mis->X = mis->DX; + mis->Y = mis->DY; + mis->DX = nx * TileSizeX + TileSizeX/2; + mis->DY = ny * TileSizeY + TileSizeY/2; + //printf( "Whirlwind new direction: %d, %d, TTL: %d\n", mis->X, mis->Y, mis->TTL ); + } + return 0; +} + +/* +** Runes controller +*/ +global int SpellRunesController( void* missile ) +{ + Unit* table[MAX_UNITS]; + int i; + int n; + int x; + int y; + + Missile* mis = (Missile*)missile; + x = mis->X / TileSizeX; + y = mis->Y / TileSizeY; + + n = SelectUnitsOnTile( x, y, table); + for( i=0; i<n; ++i ) { + if ( table[i]->Type->LandUnit ) + HitUnit(table[i],RUNE_DAMAGE); + } + + if ( mis->TTL % 100 == 0 || mis->TTL == 0 ) + { // show rune every 4 seconds (approx.) + MakeMissile( missile_rune, mis->X, mis->Y, mis->X, mis->Y ); + } + return 0; +} +/*---------------------------------------------------------------------------- +-- Functions +----------------------------------------------------------------------------*/ + +/* +** Spells constructor, inits spell id's and sounds +** +*/ +global void InitSpells() +{ + int z = 0; + while( SpellTypeTable[z].Id != -1 ) + { + SpellTypeTable[z].Id = z; + //FIXME: vladi: this won't work 'cos sound init is called after InitSpells() + SpellTypeTable[z].SoundId = SoundIdForName(SpellTypeTable[z].SoundIdent); + if( SpellTypeTable[z].SoundId == NULL ) + { + DebugLevel0Fn( "cannot get SoundId for `%s'\n", SpellTypeTable[z].SoundIdent ); //FIXME: vladi: some log level func instead of printf? + } + z++; + } + SpellTypeCount = z; + + missile_healing = MissileTypeByIdent( "missile-heal-effect" ); + missile_spell = MissileTypeByIdent( "missile-normal-spell" ); + missile_exorcism = MissileTypeByIdent( "missile-exorcism" ); + missile_explosion = MissileTypeByIdent( "missile-explosion" ); + missile_rune = MissileTypeByIdent( "missile-rune" ); +} + +/* +** Spells destructor (currently does nothing) +** +*/ +global void DoneSpells() +{ + // nothing yet +} + +/* +** Get spell id by ident +** +** @param Id Spell ident. +** +** @return spell id (index in spell type table) +*/ +global int SpellIdByIdent( const char* Ident ) +{ + int z = 0; + while( SpellTypeTable[z].Id != -1 ) + { + if ( strcmp( SpellTypeTable[z].Ident, Ident ) == 0 ) + return z; + z++; + } + return -1; +} + +/* +** Get spell type struct ptr by ident +** +** @param Id Spell ident. +** +** @return spell type struct ptr +*/ +global const SpellType* SpellTypeByIdent( const char* Ident ) +{ + int z = SpellIdByIdent( Ident ); + return z != -1 ? &(SpellTypeTable[z]) : NULL; +} + +/* +** Get spell type struct ptr by id +** +** @param Id Spell id (index in the spell type table). +** +** @return spell type struct ptr +*/ +global const SpellType* SpellTypeById( int Id ) +{ + DebugCheck( Id < 0 || Id >= SpellTypeCount ); + if ( Id < 0 || Id >= SpellTypeCount ) return NULL; + return &(SpellTypeTable[ Id ]); +} + +/* +** Check if unit can cast spell +** +** @param unit Unit that has to be checked. +** @param SpellId Spell id (index in the spell type table). +** +** @return 0 if unit cannot or 1 (=!0) if unit can cast this spell type +global int CanCastSpell( Unit* unit, int SpellId ) +{ + const SpellType* spell = SpellTypeById( SpellId ); + DebugCheck( spell == NULL ); + if ( !unit->Type->CanCastSpell ) return 0; // NOTE: this must not happen + if ( unit->Mana < spell->ManaCost ) return 0; + return 1; +} +*/ + +/* +** Spell cast! +** +** @param SpellId Spell id (index in the spell type table). +** @param unit Unit that casts the spell. +** @param target Target unit that spell is addressed to +** @param X X coord of target spot when/if target does not exist +** @param Y Y coord of target spot when/if target does not exist +** +** @return 0 if spell should/can continue or =! 0 to stop +** +*/ +global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y ) +{ + int repeat = 0; + const SpellType* spell = SpellTypeById( SpellId ); +/* + this does not work when no target unit + DebugLevel0Fn("Spell cast: %d (%s), %s -> %s (%d,%d)", + SpellId, spell->Ident, unit->Type->Name, target->Type->Name, x, y ); + printf("Spell cast: %d (%s), %s -> %s (%d,%d)\n", + SpellId, spell->Ident, unit->Type->Name, target->Type->Name, x, y ); +*/ + // the unit can collect mana during the move to target, so check is here... + + #define PLAY_FIREWORKS(s) \ + { \ + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + MakeMissile( s, x*TileSizeX+TileSizeX/2, \ + y*TileSizeX+TileSizeX/2, \ + x*TileSizeX+TileSizeX/2, \ + y*TileSizeX+TileSizeX/2 ); \ + } + + switch( spell->Action ) + { + case SpellActionNone: + DebugLevel0Fn( "No spell action" ); + break; +// ---human paladins--- + case SpellActionHolyVision: + unit->Mana -= spell->ManaCost; // get mana cost + { + Unit* u = MakeUnit(UnitTypeByIdent("unit-daemon"), unit->Player); + u->Revealer = 1; + u->HP = 2; + u->X = x; + u->Y = y; + } + break; + case SpellActionHealing: + // only can heal organic units + if (target && target->Type->Organic) + { + while( target->HP < target->Stats->HitPoints + && unit->Mana > spell->ManaCost ) + { + unit->Mana -= spell->ManaCost; // get mana cost + target->HP++; + } + PLAY_FIREWORKS(missile_healing); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionExorcism: + // exorcism works only on undead units + if ( target && target->Type->IsUndead ) + { + while( target->HP > 0 && unit->Mana > spell->ManaCost ) + { + unit->Mana -= spell->ManaCost; // get mana cost + target->HP--; + } + PLAY_FIREWORKS(missile_exorcism); + } + else + { + //FIXME: vladi: exorcism effect should be disperced on near units + } + DebugCheck( unit->Mana < 0 ); + break; +// ---human mages--- + case SpellActionFireball: + { //NOTE: fireball can be casted on spot + Missile* mis; + int sx = unit->X; + int sy = unit->Y; + int dx = x; + int dy = y; + + if ( target ) + { + dx = target->X; + dy = target->Y; + } + + unit->Mana -= spell->ManaCost; + + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + mis = MakeMissile( MissileTypeByIdent("missile-fireball"), + sx*TileSizeX+TileSizeX/2, + sy*TileSizeX+TileSizeX/2, + dx*TileSizeX+TileSizeX/2, + dy*TileSizeX+TileSizeX/2 ); + + mis->TTL = spell->TTL; + mis->Controller = SpellFireballController; + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionSlow: + if (target && !target->Type->Building && target->Slow < spell->TTL) + { + // get mana cost + unit->Mana -= spell->ManaCost; + target->Slow = spell->TTL; // about 25 sec + target->Haste = 0; + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionFlameShield: + if ( target && target->Type->Organic && target->Type->LandUnit + && target->FlameShield < spell->TTL ) + { + // get mana cost + unit->Mana -= spell->ManaCost; + target->FlameShield = spell->TTL; // about 15 sec + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionInvisibility: + if (target && target->Type->Organic && target->Invisible < spell->TTL ) + { + // get mana cost + unit->Mana -= spell->ManaCost; + target->Invisible = spell->TTL; // about 50 sec + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionPolymorph: + if ( target && target->Type->Organic ) + { + int x = target->X; + int y = target->Y; + Player* pl = target->Player; + + // as said somewhere else -- no corpses :) + RemoveUnit( target ); + UnitLost( target ); + ReleaseUnit( target ); + MakeUnitAndPlace( x, y, UnitTypeByIdent("unit-critter"), pl ); + + unit->Mana -= spell->ManaCost; + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionBlizzard: + { + /* + NOTE: vladi: blizzard differs than original in this way: + original: launches 50 shards at 5 random spots x 10 for 25 mana + here: launches 10 shards at 10 random spots x 1 for 5 mana + reason: it cannot be done w/o aditional spells list... perhasp + could be managed with fake spell with controller, but + for now it is leaved as it is... + */ + int shards = 10; + while( shards-- ) + { + Missile* mis; + int sx, sy, dx, dy; + + do + { + // find new destination in the map + dx = x + rand() % 5 - 2; + dy = y + rand() % 5 - 2; + } + while( dx < 0 && dy < 0 && dx >= TheMap.Width && dy >= TheMap.Height ); + sx = dx - 1 - rand() % 4; + sy = dy - 1 - rand() % 4; + + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + mis = MakeMissile( MissileTypeByIdent( "missile-blizzard" ), + sx*TileSizeX+TileSizeX/2, + sy*TileSizeX+TileSizeX/2, + dx*TileSizeX+TileSizeX/2, + dy*TileSizeX+TileSizeX/2 ); + mis->Damage = BLIZZARD_DAMAGE; + //FIXME: not correct -- blizzard should continue even if mage is + // destroyed (though it will be quite short time...) + mis->SourceUnit = unit; + mis->SourceType = unit->Type; + } + + unit->Mana -= spell->ManaCost; + if ( unit->Mana > spell->ManaCost ) + repeat = 1; + } + break; +// ---orc ogres--- + case SpellActionEyeOfKilrogg: + MakeUnitAndPlace( x, y, UnitTypeByIdent("unit-eye-of-kilrogg"), + unit->Player ); + + unit->Mana -= spell->ManaCost; + + PLAY_FIREWORKS(missile_spell); + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionBloodlust: + if (target && target->Type->Organic && target->Bloodlust < spell->TTL ) + { + // get mana cost + unit->Mana -= spell->ManaCost; + target->Bloodlust = spell->TTL; // about 25 sec + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionRunes: + { + //FIXME: vladi: runes should be set in formation as in original + //FIXME: vladi: runes should be set on empty tile (ground or water) + Missile* mis; + unit->Mana -= spell->ManaCost; + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + mis = MakeMissile( MissileTypeByIdent( "missile-custom" ), + x*TileSizeX+TileSizeX/2, + y*TileSizeX+TileSizeX/2, + x*TileSizeX+TileSizeX/2, + y*TileSizeX+TileSizeX/2 ); + mis->TTL = spell->TTL; + mis->Controller = SpellRunesController; + } + DebugCheck( unit->Mana < 0 ); + break; +// ---orc death knights--- + case SpellActionDeathCoil: + if( (target && target->Type->Organic) || (!target) ) + { //NOTE: fireball can be casted on spot + Missile* mis; + int sx = unit->X; + int sy = unit->Y; + int dx = x; + int dy = y; + + if ( target ) + { + dx = target->X; + dy = target->Y; + } + + unit->Mana -= spell->ManaCost; + + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + mis = MakeMissile( MissileTypeByIdent("missile-death-coil"), + sx*TileSizeX+TileSizeX/2, + sy*TileSizeX+TileSizeX/2, + dx*TileSizeX+TileSizeX/2, + dy*TileSizeX+TileSizeX/2 ); + + unit->Refs++; + mis->SourceUnit = unit; + if (target) + { + mis->TargetUnit = target; + target->Refs++; + } + mis->Controller = SpellDeathCoilController; + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionHaste: + if (target && !target->Type->Building && target->Haste < spell->TTL) + { + // get mana cost + unit->Mana -= spell->ManaCost; + target->Slow = 0; + target->Haste = spell->TTL; // about 25 sec + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionRaiseDead: + { + int i; + + for( i=0; i<NumUnits; ++i ) { + // FIXME: this tries to draw all corps, ohje + if( (Units[i]->Type->Vanishes || Units[i]->Command.Action==UnitActionDie) + && Units[i]->X == x && Units[i]->Y == y ) { + //FIXME: URGENT: remove corpse + //RemoveUnit( Units[i] ); + //UnitLost( Units[i] ); + //ReleaseUnit( Units[i] ); + MakeUnitAndPlace( x, y, UnitTypeByIdent("unit-skeleton"), + unit->Player ); + unit->Mana -= spell->ManaCost; + break; + } + } + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionWhirlwind: + { + Missile* mis; + unit->Mana -= spell->ManaCost; + + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + mis = MakeMissile( MissileTypeByIdent("missile-whirlwind"), + x*TileSizeX+TileSizeX/2, + y*TileSizeX+TileSizeX/2, + x*TileSizeX+TileSizeX/2, + y*TileSizeX+TileSizeX/2 ); + + mis->TTL = spell->TTL; + mis->Controller = SpellWhirlwindController; + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionUnholyArmor: + if (target && !target->Type->Building && target->UnholyArmor < spell->TTL) + { + // get mana cost + unit->Mana -= spell->ManaCost; + target->UnholyArmor = spell->TTL; // about 13 sec + + PLAY_FIREWORKS(missile_spell); + } + DebugCheck( unit->Mana < 0 ); + break; + case SpellActionDeathAndDecay: + { + /* + See notes about Blizzard spell above... + */ + int shards = 10; // blizzard thing, yep :) + while( shards-- ) + { + Missile* mis; + int dx, dy; + + do + { + // find new destination in the map + dx = x + rand() % 5 - 2; + dy = y + rand() % 5 - 2; + } + while( dx < 0 && dy < 0 && dx >= TheMap.Width && dy >= TheMap.Height ); + + PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ + mis = MakeMissile( MissileTypeByIdent( "missile-death-and-decay" ), + dx*TileSizeX+TileSizeX/2, + dy*TileSizeX+TileSizeX/2, + dx*TileSizeX+TileSizeX/2, + dy*TileSizeX+TileSizeX/2 ); + mis->Damage = DEATHANDDECAY_DAMAGE; + //FIXME: not correct -- blizzard should continue even if mage is + // destroyed (though it will be quite short time...) + mis->SourceUnit = unit; + mis->SourceType = unit->Type; + } + + unit->Mana -= spell->ManaCost; + if ( unit->Mana > spell->ManaCost ) + repeat = 1; + } + break; + default: + DebugLevel0Fn( "Unknown spell action" ); + break; + } + #undef PLAY_FIREWORKS + DebugCheck( unit->Mana < 0 ); + return repeat; +} + + +//@} + diff --git a/src/stratagus/stratagus.cpp b/src/stratagus/stratagus.cpp index 9a3dd76bb..06597fb4d 100644 --- a/src/stratagus/stratagus.cpp +++ b/src/stratagus/stratagus.cpp @@ -71,6 +71,7 @@ extern int getopt(int argc, char *const*argv, const char *opt); #include "sound.h" #include "network.h" #include "pathfinder.h" +#include "spells.h" /*---------------------------------------------------------------------------- -- Variables @@ -204,6 +205,7 @@ global void FreeCraftInit(void) LoadTileset(); InitUnitButtons(); LoadMissileSprites(); + InitSpells(); LoadUnitSprites(); LoadConstructions(); LoadDecorations(); diff --git a/src/ui/botpanel.cpp b/src/ui/botpanel.cpp index 6c21220b3..9a2c58c3a 100644 --- a/src/ui/botpanel.cpp +++ b/src/ui/botpanel.cpp @@ -50,6 +50,7 @@ #include "map.h" #include "unit.h" #include "font.h" +#include "spells.h" /*---------------------------------------------------------------------------- -- Defines @@ -142,12 +143,12 @@ int AddButton( int pos, int level, const char* IconIdent, ba->ValueStr = strdup( value ); switch( action ) { - case B_Magic: ba->Value = UpgradeIdByIdent( value ); break; - case B_Train: ba->Value = UnitTypeIdByIdent( value ); break; - case B_Research: ba->Value = UpgradeIdByIdent( value ); break; - case B_UpgradeTo: ba->Value = UnitTypeIdByIdent( value ); break; - case B_Build: ba->Value = UnitTypeIdByIdent( value ); break; - default: ba->Value = atoi( value ); break; + case B_SpellCast: ba->Value = SpellIdByIdent( value ); break; + case B_Train: ba->Value = UnitTypeIdByIdent( value ); break; + case B_Research: ba->Value = UpgradeIdByIdent( value ); break; + case B_UpgradeTo: ba->Value = UnitTypeIdByIdent( value ); break; + case B_Build: ba->Value = UnitTypeIdByIdent( value ); break; + default: ba->Value = atoi( value ); break; } } else { ba->ValueStr = NULL; @@ -297,9 +298,11 @@ global void DrawButtonPanel(void) case B_Research: SetCosts(0,Upgrades[v].Costs); break; - case B_Magic: - // FIXME: correct costs!!! - SetCosts(11,NULL); + case B_SpellCast: + { + const SpellType* spell = SpellTypeById( v ); + SetCosts(spell->ManaCost,NULL); + } break; default: @@ -521,7 +524,7 @@ global void UpdateButtonPanel(void) allow=UpgradeIdentAllowed( ThisPlayer, UnitButtonTable[z]->ValueStr )=='A'; } - } else if ( UnitButtonTable[z]->Action == B_Magic ) { + } else if ( UnitButtonTable[z]->Action == B_SpellCast ) { DebugLevel3("Magic: %d,%c\n", CheckDependByIdent( ThisPlayer, UnitButtonTable[z]->ValueStr ), @@ -600,9 +603,11 @@ global void DoButtonButtonClicked(int button) case B_Repair: case B_AttackGround: case B_Demolish: + case B_SpellCast: CursorState=CursorStateSelect; GameCursor=&Cursors[CursorTypeYellowHair]; CursorAction=CurrentButtons[button].Action; + CursorValue=CurrentButtons[button].Value; CurrentButtonLevel=9; // level 9 is cancel-only UpdateButtonPanel(); MustRedraw|=RedrawCursor; diff --git a/src/ui/button_table.cpp b/src/ui/button_table.cpp index a54827f63..e6467e3b6 100644 --- a/src/ui/button_table.cpp +++ b/src/ui/button_table.cpp @@ -353,20 +353,20 @@ global ButtonAction AllButtons[] = { // paladin specific actions -------------------------------------------- { 7, 0, { "icon-holy-vision" }, - B_Magic, 0, "upgrade-holy-vision", - NULL, NULL, + B_SpellCast, 0, "spell-holy-vision", + CheckTrue, NULL, 'v', "HOLY ~!VISION", "unit-paladin" }, { 8, 0, { "icon-heal" }, - B_Magic, 0, "upgrade-healing", - NULL, NULL, + B_SpellCast, 0, "spell-healing", + CheckUpgrade, "upgrade-healing", 'h', "~!HEALING (per 1 HP)", "unit-paladin" }, { 9, 0, { "icon-exorcism" }, - B_Magic, 0, "upgrade-exorcism", - NULL, NULL, + B_SpellCast, 0, "spell-exorcism", + CheckUpgrade, "upgrade-exorcism", 'e', "~!EXORCISM", "unit-paladin" }, @@ -378,38 +378,38 @@ global ButtonAction AllButtons[] = { "unit-mage,unit-khadgar" }, { 4, 0, { "icon-fireball" }, - B_Magic, 0, "upgrade-fireball", - NULL, NULL, + B_SpellCast, 0, "spell-fireball", + CheckTrue, NULL, 'f', "~!FIREBALL", "unit-mage,unit-khadgar" }, { 5, 0, { "icon-slow" }, - B_Magic, 0, "upgrade-slow", - NULL, NULL, + B_SpellCast, 0, "spell-slow", + CheckUpgrade, "upgrade-slow", 'o', "SL~!OW", "unit-mage" }, { 6, 0, { "icon-flame-shield" }, - B_Magic, 0, "upgrade-flame-shield", - NULL, NULL, + B_SpellCast, 0, "spell-flame-shield", + CheckUpgrade, "upgrade-flame-shield", 'l', "F~!LAME SHIELD", "unit-mage" }, { 7, 0, { "icon-invisibility" }, - B_Magic, 0, "upgrade-invisibility", - NULL, NULL, + B_SpellCast, 0, "spell-invisibility", + CheckUpgrade, "upgrade-invisibility", 'i', "~!INVISIBILITY", "unit-mage" }, { 8, 0, { "icon-polymorph" }, - B_Magic, 0, "upgrade-polymorph", - NULL, NULL, + B_SpellCast, 0, "spell-polymorph", + CheckUpgrade, "upgrade-polymorph", 'p', "~!POLYMORPH", "unit-mage" }, { 9, 0, { "icon-blizzard" }, - B_Magic, 0, "upgrade-blizzard", - NULL, NULL, + B_SpellCast, 0, "spell-blizzard", + CheckUpgrade, "upgrade-blizzard", 'b', "~!BLIZZARD", "unit-mage" }, @@ -1080,37 +1080,38 @@ global ButtonAction AllButtons[] = { // ogre-mage specific actions -------------------------------------------- { 7, 0, { "icon-eye-of-kilrogg" }, - B_Magic, 0, "upgrade-eye-of-kilrogg", - NULL, NULL, + B_SpellCast, 0, "spell-eye-of-kilrogg", + CheckTrue, NULL, 'k', "EYE OF ~!KILROGG", "unit-ogre-mage" }, { 8, 0, { "icon-bloodlust" }, - B_Magic, 0, "upgrade-bloodlust", - NULL, NULL, + B_SpellCast, 0, "spell-bloodlust", + CheckUpgrade, "upgrade-bloodlust", 'b', "~!BLOODLUST", "unit-ogre-mage" }, { 9, 0, { "icon-runes" }, - B_Magic, 0, "upgrade-runes", - NULL, NULL, + B_SpellCast, 0, "spell-runes", + CheckTrue, "upgrade-runes", 'r', "~!RUNES", "unit-ogre-mage" }, +// cho'gall specific actions --- same as ogre mage but it has them always -- { 7, 0, { "icon-eye-of-kilrogg" }, - B_Magic, 0, "upgrade-eye-of-kilrogg", + B_SpellCast, 0, "spell-eye-of-kilrogg", CheckTrue, NULL, 'k', "EYE OF ~!KILROGG", "unit-cho'gall" }, { 8, 0, { "icon-bloodlust" }, - B_Magic, 0, "upgrade-bloodlust", + B_SpellCast, 0, "spell-bloodlust", CheckTrue, NULL, 'b', "~!BLOODLUST", "unit-cho'gall" }, { 9, 0, { "icon-runes" }, - B_Magic, 0, "upgrade-runes", + B_SpellCast, 0, "spell-runes", CheckTrue, NULL, 'r', "~!RUNES", "unit-cho'gall" @@ -1123,38 +1124,38 @@ global ButtonAction AllButtons[] = { "unit-death-knight" ",unit-gul'dan" ",unit-teron-gorefiend" }, { 4, 0, { "icon-death-coil" }, - B_Magic, 0, "upgrade-death-coil", - NULL, NULL, + B_SpellCast, 0, "spell-death-coil", + CheckTrue, NULL, 'c', "DEATH ~!COIL", "unit-death-knight" ",unit-gul'dan" ",unit-teron-gorefiend" }, { 5, 0, { "icon-haste" }, - B_Magic, 0, "upgrade-haste", - NULL, NULL, + B_SpellCast, 0, "spell-haste", + CheckUpgrade, "upgrade-haste", 'h', "~!HASTE", "unit-death-knight" }, { 6, 0, { "icon-raise-dead" }, - B_Magic, 0, "upgrade-raise-dead", - NULL, NULL, + B_SpellCast, 0, "spell-raise-dead", + CheckUpgrade, "upgrade-raise-dead", 'r', "~!RAISE DEAD", "unit-death-knight" }, { 7, 0, { "icon-whirlwind" }, - B_Magic, 0, "upgrade-whirlwind", - NULL, NULL, + B_SpellCast, 0, "spell-whirlwind", + CheckUpgrade, "upgrade-whirlwind", 'w', "~!WHIRLWIND", "unit-death-knight" }, { 8, 0, { "icon-unholy-armor" }, - B_Magic, 0, "upgrade-unholy-armor", - NULL, NULL, + B_SpellCast, 0, "spell-unholy-armor", + CheckUpgrade, "upgrade-unholy-armor", 'u', "~!UNHOLY ARMOR", "unit-death-knight" }, { 9, 0, { "icon-death-and-decay" }, - B_Magic, 0, "upgrade-death-and-decay", - NULL, NULL, + B_SpellCast, 0, "spell-death-and-decay", + CheckUpgrade, "upgrade-death-and-decay", 'd', "~!DEATH AND DECAY", "unit-death-knight" }, diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp index ddb68ea7b..807487909 100644 --- a/src/ui/mainscr.cpp +++ b/src/ui/mainscr.cpp @@ -1,6 +1,6 @@ // ___________ _________ _____ __ // \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_ -// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ +// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\ // | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | | // \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__| // \/ \/ \/ \/ \/ @@ -513,7 +513,7 @@ global void ShiftMessages(void) { strcpy( Messages[z], "" ); } - MessagesCount--; + MessagesCount--; } /** @@ -534,7 +534,7 @@ global void ShiftMessagesEvent(void) MessagesEventX[z] = -1; MessagesEventY[z] = -1; } - MessagesCount--; + MessagesCount--; } /** @@ -571,7 +571,7 @@ global void SetMessage( char* fmt, ... ) va_end( va ); if ( MessagesCount == MESSAGES_MAX ) ShiftMessages(); - strcpy( Messages[ MessagesCount ], temp ); + strcpy( Messages[ MessagesCount ], temp ); MessagesCount++; MustRedraw|=RedrawMessage|RedrawMap; MessageFrameTimeout = FrameCounter + MESSAGES_TIMEOUT; @@ -596,16 +596,16 @@ global void SetMessage2( int x, int y, char* fmt, ... ) va_end( va ); if ( MessagesCount == MESSAGES_MAX ) ShiftMessages(); - strcpy( Messages[ MessagesCount ], temp ); + strcpy( Messages[ MessagesCount ], temp ); MessagesCount++; - + if ( MessagesEventCount == MESSAGES_MAX ) ShiftMessagesEvent(); strcpy( MessagesEvent[ MessagesEventCount ], temp ); MessagesEventX[ MessagesEventCount ] = x; MessagesEventY[ MessagesEventCount ] = y; MessagesEventCount++; - + MustRedraw|=RedrawMessage|RedrawMap; MessageFrameTimeout = FrameCounter + MESSAGES_TIMEOUT; } @@ -664,8 +664,8 @@ global void CenterOnMessage(void) MessagesEventIndex = 0; if ( MessagesEventIndex >= MessagesEventCount ) return; - MapCenter( MessagesEventX[ MessagesEventIndex ], - MessagesEventY[ MessagesEventIndex ] ); + MapCenter( MessagesEventX[ MessagesEventIndex ], + MessagesEventY[ MessagesEventIndex ] ); SetMessage( "*Event: %s", MessagesEvent[ MessagesEventIndex ] ); MessagesEventIndex++; } diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp index 4cc775eb6..06c575e07 100644 --- a/src/ui/mouse.cpp +++ b/src/ui/mouse.cpp @@ -771,6 +771,43 @@ local void SendUnload(int x,int y) } } +/** +** Send the current selected group for spell cast. +** +** To empty field: +** To unit: +** Spell cast on unit or on map spot. +** +** @param x X map tile position. +** @param y Y map tile position. +** +** @see Selected, @see NumSelected +*/ +local void SendSpellCast(int x,int y) +{ + int i; + Unit* unit; + Unit* dest; + + dest=UnitOnMapTile(x,y); + DebugLevel3Fn("SpellCast on: %p (%d,%d)\n", dest, x, y); + /* NOTE: Vladi: + This is a high-level function, it sends target spot and unit + (if exists). All checks are performed at spell cast handle + function which will cancel function if cannot be executed + */ + for( i=0; i<NumSelected; i++ ) { + unit=Selected[i]; + if( !unit->Type->CanCastSpell ) + continue; // this unit cannot cast spell + if( dest && unit == dest ) + continue; // no unit can cast spell on himself + // CursorValue here holds the spell type id + SendCommandSpellCast(unit,x,y,dest,CursorValue,!(KeyModifiers&ModifierShift)); + } +} + + /** ** Send a command to selected units. ** @@ -808,6 +845,9 @@ local void SendCommand(int x,int y) case B_Demolish: SendDemolish(x,y); break; + case B_SpellCast: + SendSpellCast(x,y); + break; default: DebugLevel1("Unsupported send action %d\n",CursorAction); break; diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 45e17bdc8..6112576cd 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -271,6 +271,7 @@ global Unit* MakeUnit(UnitType* type,Player* player) unit->Reset=1; unit->Rs=MyRand()%100; // used for random fancy buildings and other things + unit->Revealer = 0; // FOW revealer unit->Command.Action=UnitActionStill; unit->PendCommand.Action=UnitActionStill; @@ -735,6 +736,9 @@ global int UnitVisible(const Unit* unit) ** ** NOTE: we could build a table of all magic units reducing cpu use. */ +//FIXME: vladi: the doc says incrementing mana is done by 1 per second +// the spells effect can be decremented at the same time and this +// will reduse calls to this function to one time per second only! global void UnitIncrementMana(void) { Unit** table; @@ -758,6 +762,20 @@ global void UnitIncrementMana(void) MustRedraw|=RedrawInfoPanel; } } + + // + // decrease spells effects time + // + if ( unit->Bloodlust > 0 ) + unit->Bloodlust--; + if ( unit->Haste > 0 ) + unit->Haste--; + if ( unit->Slow > 0 ) + unit->Slow--; + if ( unit->Invisible > 0 ) + unit->Invisible--; + if ( unit->UnholyArmor > 0 ) + unit->UnholyArmor--; } } @@ -2018,7 +2036,9 @@ global void DestroyUnit(Unit* unit) // FIXME: units in transporters should die without corpes... if( unit->Type->Transporter ) { // Transporters loose their units - DestroyAllInside(unit); + //FIXME: vladi: it could be usefull if transport is near land + // to unload instead of destroying all units in it... ? + DestroyAllInside(unit); } RemoveUnit(unit); @@ -2578,12 +2598,13 @@ global void SaveUnit(const Unit* unit,FILE* file) fprintf(file,"\t%d ",unit->Player->Player); fprintf(file,"%d ",unit->Mana); fprintf(file,"%d ",unit->HP); - fprintf(file,"(%d %d %d %d %d)\n" + fprintf(file,"(%d %d %d %d %d %d)\n" ,unit->Bloodlust ,unit->Haste ,unit->Slow ,unit->Invisible - ,unit->Shield); + ,unit->FlameShield + ,unit->UnholyArmor); fprintf(file,"\t%d ",unit->GroupId); fprintf(file,"%d\n",unit->Value); diff --git a/src/unit/unit_draw.cpp b/src/unit/unit_draw.cpp index 1b419ce69..4141eb73d 100644 --- a/src/unit/unit_draw.cpp +++ b/src/unit/unit_draw.cpp @@ -60,10 +60,6 @@ global int ShowHealthHorizontal=1; global int ShowManaHorizontal=1; /// Flag: show bars and dot energy only for selected global int ShowEnergySelectedOnly; - /// Flag: show health bar always full sized -global int ShowFullSizedHealthBar=1; - /// Flag: show mana bar always full sized -global int ShowFullSizedManaBar=1; // FIXME: not all variables of this file are here // FIXME: perhaps split this file into two? @@ -277,6 +273,11 @@ global Decoration ShadowSprite = { "graphic/unit shadow.png", 0,42, 32,32 }; +/** +** Sprite to display the active spells on units. +*/ +global Graphic* SpellSprites; + #if defined(USE_CCL) || defined(USE_CCL2) /** @@ -334,6 +335,7 @@ global void LoadDecorations(void) ,ManaSprite.Width,ManaSprite.Height); ShadowSprite.Sprite=LoadSprite(ShadowSprite.File ,ShadowSprite.Width,ShadowSprite.Height); + SpellSprites=LoadSprite("graphic/bloodlust,haste,slow,invis.,shield.png",16,16); } /** @@ -348,8 +350,6 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) { int f; int color; - int deco_x; - int deco_y; UnitStats* stats; @@ -360,13 +360,11 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) return; } - // FIXME: Should split this into small functions? - // - // Health bar of the unit. + // Health bar on left side of unit. // stats=unit->Stats; - if( ShowHealthBar ) { + if( !type->Critter && ShowHealthBar ) { if( stats->HitPoints && !(ShowNoFull && unit->HP==stats->HitPoints) ) { f=(100*unit->HP)/stats->HitPoints; @@ -377,31 +375,26 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) } else { color=ColorRed; } - if( ShowHealthHorizontal ) { - deco_x=x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1; - deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2 - +type->BoxHeight; - if( !ShowFullSizedHealthBar ) { + if ( ShowHealthHorizontal == 0) { + VideoFillRectangleClip(color + ,x+(type->TileWidth*TileSizeX + -type->BoxWidth)/2 + ,y+(type->TileHeight*TileSizeY + -type->BoxHeight)/2 + ,2,(f*type->BoxHeight)/100); + } else { VideoFillRectangleClip(ColorBlack - ,deco_x,deco_y,type->BoxWidth+2,4); - } else { - VideoDrawRectangleClip(ColorBlack - ,deco_x,deco_y,(f*type->BoxWidth)/100+1,3); - } - VideoFillRectangleClip(color - ,deco_x+1,deco_y+1,(f*type->BoxWidth)/100,2); - } else { - deco_x=x+(type->TileWidth*TileSizeX-type->BoxWidth)/2-3; - deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2; - if( ShowFullSizedHealthBar ) { - VideoFillRectangleClip(ColorBlack - ,deco_x,deco_y,4,type->BoxHeight+1); - } else { - VideoDrawRectangleClip(ColorBlack - ,deco_x,deco_y,3,(f*type->BoxHeight)/100); - } - VideoFillRectangleClip(color - ,deco_x+1,deco_y+1,2,(f*type->BoxHeight)/100-1); + ,x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1 + ,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2) + +type->BoxHeight+1 + ,((f*type->BoxHeight)/100)+2 + ,5); + VideoFillRectangleClip(color + ,x+((type->TileWidth*TileSizeX-type->BoxWidth)/2) + ,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2) + +type->BoxHeight+2 + ,(f*type->BoxHeight)/100 + ,3); } } } @@ -409,9 +402,11 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) // // Health dot on left side of unit. // - if( ShowHealthDot ) { + if( !type->Critter && ShowHealthDot ) { if( stats->HitPoints && !(ShowNoFull && unit->HP==stats->HitPoints) ) { + int x1; + int y1; int n; f=(100*unit->HP)/stats->HitPoints; @@ -426,61 +421,55 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) } DebugCheck( n<0 ); if( HealthSprite.HotX<0 ) { - deco_x=x+HealthSprite.HotX + x1=x+HealthSprite.HotX +(type->TileWidth*TileSizeX +type->BoxWidth+1)/2; } else { - deco_x=x-HealthSprite.HotX + x1=x-HealthSprite.HotX +(type->TileWidth*TileSizeX -type->BoxWidth+1)/2; } if( HealthSprite.HotY<0 ) { - deco_y=y+HealthSprite.HotY + y1=y+HealthSprite.HotY +(type->TileHeight*TileSizeY +type->BoxHeight+1)/2; } else { - deco_y=y-HealthSprite.HotY + y1=y-HealthSprite.HotY +(type->TileHeight*TileSizeY -type->BoxHeight+1)/2; } - VideoDrawClip(HealthSprite.Sprite,n,deco_x,deco_y); + VideoDrawClip(HealthSprite.Sprite,n,x1,y1); } } // - // Mana bar for the unit. + // Mana bar on right side of unit. // if( ShowManaBar ) { if( type->CanCastSpell && !(ShowNoFull && unit->Mana==255) ) { - if( ShowManaHorizontal ) { - f=(100*unit->Mana)/255; - deco_x=x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1; - deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2 - +type->BoxHeight+4; - if( ShowFullSizedManaBar ) { - VideoFillRectangleClip(ColorBlack - ,deco_x,deco_y,type->BoxHeight+2,4); - } else { - VideoDrawRectangleClip(ColorBlack - ,deco_x,deco_y,(f*type->BoxHeight)/100+1,3); - } - VideoFillRectangleClip(ColorBlue - ,deco_x+1,deco_y+1,(f*type->BoxHeight)/100,2); - } else { - deco_x=x+(type->TileWidth*TileSizeX+type->BoxWidth)/2; - deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2; + if ( ShowManaHorizontal == 0) { f=(100*unit->Mana)/255; - if( ShowFullSizedManaBar ) { + VideoFillRectangleClip(ColorBlue + ,x+(type->TileWidth*TileSizeX + +type->BoxWidth)/2 + ,y+(type->TileHeight*TileSizeY + -type->BoxHeight)/2 + ,2,(f*type->BoxHeight)/100); + } else { + f=(100*unit->Mana)/255; VideoFillRectangleClip(ColorBlack - ,deco_x,deco_y,4,type->BoxHeight+1); - } else { - VideoDrawRectangleClip(ColorBlack - ,deco_x,deco_y,3,(f*type->BoxHeight)/100); - } - VideoFillRectangleClip(ColorBlue - ,deco_x+1,deco_y+1,2,(f*type->BoxHeight)/100-1); + ,x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1 + ,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2)+type->BoxHeight+6 + ,(type->BoxHeight)+2 + ,5); + VideoFillRectangleClip(ColorBlue + ,x+(type->TileWidth*TileSizeX-type->BoxWidth)/2 + ,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2)+type->BoxHeight+7 + ,(f*type->BoxHeight)/100 + ,3); } + } } @@ -490,6 +479,8 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) if( ShowManaDot ) { if( type->CanCastSpell && !(ShowNoFull && unit->Mana==255) ) { + int x1; + int y1; int n; f=(100*unit->Mana)/255; @@ -507,27 +498,41 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y) n=4; } if( ManaSprite.HotX<0 ) { - deco_x=x+ManaSprite.HotX + x1=x+ManaSprite.HotX +(type->TileWidth*TileSizeX +type->BoxWidth+1)/2; } else { - deco_x=x-ManaSprite.HotX + x1=x-ManaSprite.HotX +(type->TileWidth*TileSizeX -type->BoxWidth+1)/2; } if( ManaSprite.HotY<0 ) { - deco_y=y+ManaSprite.HotY + y1=y+ManaSprite.HotY +(type->TileHeight*TileSizeY +type->BoxHeight+1)/2; } else { - deco_y=y-ManaSprite.HotY + y1=y-ManaSprite.HotY +(type->TileHeight*TileSizeY -type->BoxHeight+1)/2; } - VideoDrawClip(ManaSprite.Sprite,n,deco_x,deco_y); + VideoDrawClip(ManaSprite.Sprite,n,x1,y1); } } + // + // Draw spells decoration + // + if ( unit->Bloodlust > 0 ) + VideoDrawClip( SpellSprites, 0, x, y ); + if ( unit->Haste > 0 ) // same slot as slow + VideoDrawClip( SpellSprites, 1, x+16, y ); + if ( unit->Slow > 0 ) // same slot as haste + VideoDrawClip( SpellSprites, 2, x+16, y ); + if ( unit->Invisible > 0 ) + VideoDrawClip( SpellSprites, 3, x+16+16, y ); + if ( unit->UnholyArmor > 0 ) + VideoDrawClip( SpellSprites, 4, x+16+16+16, y ); + // FIXME: group number could also be shown } @@ -559,7 +564,6 @@ local void DrawShadow(Unit* unit,UnitType* type,int x,int y) ,x+ShadowSprite.HotX,y+ShadowSprite.HotY); } -#if 0 /** ** Draw path from current postion to the destination of the move. ** @@ -681,134 +685,15 @@ global void DrawPath(Unit* unit) ,6,6); } } -#endif /** ** Show the current order of an unit. ** ** @param unit Pointer to the unit. +** */ local void ShowOrder(const Unit* unit) { -#ifdef NEW_ORDERS - int x1; - int y1; - int x2; - int y2; - int color; - const Unit* goal; - int i; - - if( unit->Destroyed ) { - return; - } - x1=Map2ScreenX(unit->X)+unit->IX+unit->Type->TileWidth*TileSizeX/2; - y1=Map2ScreenY(unit->Y)+unit->IY+unit->Type->TileHeight*TileSizeY/2; - VideoFillCircleClip(ColorGreen,x1,y1,1); - - for( i=0; i<=unit->OrderCount; ++i ) { - if( (goal=unit->Orders[i].Goal) ) { - x2=Map2ScreenX(goal->X)+goal->IX+goal->Type->TileWidth*TileSizeX/2; - y2=Map2ScreenY(goal->Y)+goal->IY+goal->Type->TileHeight*TileSizeY/2; - } else { - x2=Map2ScreenX(unit->Orders[i].X)+TileSizeX/2; - y2=Map2ScreenY(unit->Orders[i].Y)+TileSizeY/2; - } - switch( unit->Orders[i].Action ) { - case UnitActionNone: - color=ColorGray; - break; - - case UnitActionStill: - color=ColorGray; - break; - - case UnitActionStandGround: - color=ColorGray; - break; - - case UnitActionFollow: - case UnitActionMove: - color=ColorGreen; - break; - - case UnitActionPatrol: - VideoDrawLineClip(ColorGreen,x1,y1,x2,y2); - color=ColorBlue; - break; - - case UnitActionRepair: - color=ColorGreen; - break; - - case UnitActionAttack: - case UnitActionAttackGround: - color=ColorRed; - break; - - case UnitActionBoard: - color=ColorGreen; - break; - - case UnitActionUnload: - color=ColorGreen; - break; - - case UnitActionDie: - color=ColorGray; - break; - - case UnitActionTrain: - color=ColorGray; - break; - - case UnitActionUpgradeTo: - color=ColorGray; - break; - - case UnitActionResearch: - color=ColorGray; - break; - - case UnitActionBuild: - color=ColorGreen; - break; - - case UnitActionBuilded: - color=ColorGray; - break; - - case UnitActionHarvest: - color=ColorGreen; - break; - - case UnitActionMineGold: - color=ColorGreen; - break; - - case UnitActionHaulOil: - color=ColorGreen; - break; - - case UnitActionReturnGoods: - color=ColorGreen; - break; - - case UnitActionDemolish: - color=ColorRed; - break; - - default: - color=ColorGray; - DebugLevel1Fn("Unknown action %d\n",unit->Orders[i].Action); - break; - } - VideoDrawLineClip(color,x1,y1,x2,y2); - VideoFillCircleClip(color,x2,y2,2); - x1=x2; - y1=y2; - } -#else int x1; int y1; int x2; @@ -925,7 +810,6 @@ local void ShowOrder(const Unit* unit) VideoFillCircleClip(color,x2,y2,2); //DrawPath(unit); -#endif } /* @@ -933,7 +817,6 @@ local void ShowOrder(const Unit* unit) ** ** 1) Must draw underground/underwater units. (FUTURE extension) ** 2) Must draw buildings and corpse. -** FIXME: Bridges? ** 3) Must draw land/sea units. ** 4) Must draw decoration units. (FUTURE extension) ** 5) Must draw low air units. @@ -998,34 +881,6 @@ local void DrawBuilding(Unit* unit) DrawSelection(unit,type,x,y); #endif -#ifdef NEW_ORDERS - // - // Buildings under construction/upgrade/ready. - // - if( unit->Orders[0].Action==UnitActionBuilded ) { - if( unit->Constructed || VideoGraphicFrames(type->Sprite)<=1 ) { - DrawConstruction(type->OverlapFrame - ,frame&127 - ,x+(type->TileWidth*TileSizeX)/2 - ,y+(type->TileHeight*TileSizeY)/2); - } else { - DrawUnitType(type,frame,x,y); - } - } else if( unit->Orders[0].Action==UnitActionUpgradeTo ) { - // FIXME: update frame hard coded. - DrawUnitType(unit->Orders[0].Type,(frame&128)+1,x,y); - } else { - DrawUnitType(type,frame,x,y); - } - - // FIXME: johns: ugly check here should be removed! - if( unit->Orders[0].Action!=UnitActionDie ) { - DrawDecoration(unit,type,x,y); -#if 0 - DrawSelection(unit,type,x,y); -#endif - } -#else // // Buildings under construction/upgrade/ready. // @@ -1044,12 +899,16 @@ local void DrawBuilding(Unit* unit) DrawUnitType(type,frame,x,y); } +#if 0 // FIXME: johns: ugly check here should be removed! if( unit->Command.Action!=UnitActionDie ) { DrawDecoration(unit,type,x,y); -#if 0 DrawSelection(unit,type,x,y); -#endif + } +#else + // FIXME: johns: ugly check here should be removed! + if( unit->Command.Action!=UnitActionDie ) { + DrawDecoration(unit,type,x,y); } #endif } @@ -1067,6 +926,8 @@ local void DrawUnit(Unit* unit) UnitType* type; UnitStats* stats; + if ( unit->Revealer ) return; // Revealers are not drawn + type=unit->Type; x=Map2ScreenX(unit->X)+unit->IX; @@ -1124,17 +985,10 @@ local void DrawUnit(Unit* unit) ShowOrder(unit); } -#ifdef NEW_ORDERS - // FIXME: johns: ugly check here should be removed! - if( unit->Orders[0].Action!=UnitActionDie ) { - DrawDecoration(unit,type,x,y); - } -#else // FIXME: johns: ugly check here should be removed! if( unit->Command.Action!=UnitActionDie ) { DrawDecoration(unit,type,x,y); } -#endif } /** @@ -1159,17 +1013,9 @@ global void DrawUnits(void) for( i=0; i<NumUnits; ++i ) { unit=Units[i]; // FIXME: this tries to draw all corps, ohje -#ifdef NEW_ORDERS - if( (unit->Type->Vanishes || unit->Orders[0].Action==UnitActionDie) - && UnitVisible(unit) ) { + if( unit->Type->Vanishes || unit->Command.Action==UnitActionDie ) { DrawUnit(unit); } -#else - if( (unit->Type->Vanishes || unit->Command.Action==UnitActionDie) - && UnitVisible(unit) ) { - DrawUnit(unit); - } -#endif } // // 2b) buildings diff --git a/src/unit/unit_find.cpp b/src/unit/unit_find.cpp index b3697ec95..ae62e710a 100644 --- a/src/unit/unit_find.cpp +++ b/src/unit/unit_find.cpp @@ -67,7 +67,10 @@ */ global int SelectUnits(int x1,int y1,int x2,int y2,Unit** table) { - return UnitCacheSelect(x1,y1,x2,y2,table); + if ( x1 == x2 && y1 == y2 ) + return UnitCacheOnTile(x1,y1,table); + else + return UnitCacheSelect(x1,y1,x2,y2,table); } /** diff --git a/src/video/cursor.cpp b/src/video/cursor.cpp index cd3c5dd2e..e6aa0a30d 100644 --- a/src/video/cursor.cpp +++ b/src/video/cursor.cpp @@ -124,6 +124,7 @@ global CursorType Cursors[CursorMax] = { global enum CursorState_e CursorState; /// cursor state global int CursorAction; /// action for selection +global int CursorValue; /// value for CursorAction (spell type f.e.) global UnitType* CursorBuilding; /// building cursor global CursorType* GameCursor; /// cursor type