From 1912d10a566fe5cea1b4e1d26180518c3b1420eb Mon Sep 17 00:00:00 2001 From: johns <> Date: Mon, 24 Apr 2000 15:48:57 +0000 Subject: [PATCH] New enemy detection code and rescue of units implemented, bug in change unit owner fixed --- include/sound.h | 3 + include/unit.h | 8 ++ sound/script_sound.cpp | 4 + sound/sound.cpp | 12 ++- stratagus/mainloop.cpp | 70 +++++++++------ stratagus/player.cpp | 52 +++++++++++ ui/mouse.cpp | 5 +- unit/unit.cpp | 191 ++++++++++++++++++++++++++++++++++++++++- 8 files changed, 314 insertions(+), 31 deletions(-) diff --git a/include/sound.h b/include/sound.h index 190cf8e2c..c50b6200c 100644 --- a/include/sound.h +++ b/include/sound.h @@ -50,6 +50,9 @@ typedef struct _game_sound_ { SoundConfig HumanWorkComplete; /// building ready SoundConfig PeasantWorkComplete; /// building ready SoundConfig OrcWorkComplete; /// building ready + + SoundConfig HumanRescue; /// rescue units + SoundConfig OrcRescue; /// rescue units } GameSound; /*---------------------------------------------------------------------------- diff --git a/include/unit.h b/include/unit.h index 84519dfc6..fd37178b1 100644 --- a/include/unit.h +++ b/include/unit.h @@ -355,7 +355,13 @@ extern void UpdateForNewUnit(const Unit* unit,int upgrade); extern void NearestOfUnit(const Unit* unit,int tx,int ty,int *dx,int *dy); extern int UnitVisible(const Unit* unit); extern void RemoveUnit(Unit* unit); + /// Increment mana of all magic units each second. extern void UnitIncrementMana(void); + /// Increment health of all regenerating units each second. +extern void UnitIncrementHealth(void); + /// Check for rescue each second. +extern void RescueUnits(void); + /// Change owner of unit. extern void ChangeUnitOwner(Unit* unit,Player* old,Player* new); extern void UnitNewHeading(Unit* unit); @@ -397,6 +403,8 @@ extern int ViewPointDistanceToUnit(Unit* dest); /// Return true, if unit is an enemy of the player extern int IsEnemy(const Player* player,const Unit* dest); + /// Return true, if unit is allied with the player +extern int IsAllied(const Player* player,const Unit* dest); extern int CanTarget(const UnitType* type,const UnitType* dest); extern void SaveUnit(const Unit* unit,FILE* file); /// save unit-structure diff --git a/sound/script_sound.cpp b/sound/script_sound.cpp index 29f167d1f..14463e71a 100644 --- a/sound/script_sound.cpp +++ b/sound/script_sound.cpp @@ -270,6 +270,10 @@ local SCM CclDefineGameSounds(SCM list) { } else if ( gh_eq_p(name,gh_symbol2scm("placement-success")) ) { GameSounds.PlacementSuccess.Sound=CCL_SOUND_ID(data); DebugLevel3("SoundPlacementSuccess %d\n",SoundPlacementSuccess); + } else if ( gh_eq_p(name,gh_symbol2scm("human-rescue")) ) { + GameSounds.HumanRescue.Sound=CCL_SOUND_ID(data); + } else if ( gh_eq_p(name,gh_symbol2scm("orc-rescue")) ) { + GameSounds.OrcRescue.Sound=CCL_SOUND_ID(data); } else { fprintf(stderr,"Incorrect symbol\n"); return list; diff --git a/sound/sound.cpp b/sound/sound.cpp index 4e362c04a..3fb42e438 100644 --- a/sound/sound.cpp +++ b/sound/sound.cpp @@ -63,7 +63,9 @@ global GameSound GameSounds={ { "building construction"}, { "basic human voices work complete" }, { "peasant work complete" }, - { "basic orc voices work complete" } + { "basic orc voices work complete" }, + { "rescue (human)" }, + { "rescue (orc)" }, }; /*---------------------------------------------------------------------------- @@ -277,6 +279,14 @@ global void InitSoundClient(void) GameSounds.OrcWorkComplete.Sound= SoundIdForName(GameSounds.OrcWorkComplete.Name); } + if( !GameSounds.HumanRescue.Sound ) { + GameSounds.HumanRescue.Sound= + SoundIdForName(GameSounds.HumanRescue.Name); + } + if( !GameSounds.OrcRescue.Sound ) { + GameSounds.OrcRescue.Sound= + SoundIdForName(GameSounds.OrcRescue.Name); + } } #endif // } WITH_SOUND diff --git a/stratagus/mainloop.cpp b/stratagus/mainloop.cpp index e9423d656..b331602a7 100644 --- a/stratagus/mainloop.cpp +++ b/stratagus/mainloop.cpp @@ -9,14 +9,17 @@ // FreeCraft - A free fantasy real time strategy game engine // /**@name mainloop.c - The main game loop. */ -/* -** (c) Copyright 1998-2000 by Lutz Sammer -** -** $Id$ -*/ +// +// (c) Copyright 1998-2000 by Lutz Sammer +// +// $Id$ //@{ +//---------------------------------------------------------------------------- +// Includes +//---------------------------------------------------------------------------- + #include <stdio.h> #include "freecraft.h" @@ -44,18 +47,30 @@ #include <SDL/SDL.h> #endif -/* variable set when we are scrolling via keyboard */ +//---------------------------------------------------------------------------- +// Variables +//---------------------------------------------------------------------------- + + /// variable set when we are scrolling via keyboard global enum _scroll_state_ KeyScrollState=ScrollNone; -/* variable set when we are scrolling via mouse */ + /// variable set when we are scrolling via mouse global enum _scroll_state_ MouseScrollState=ScrollNone; +//---------------------------------------------------------------------------- +// Functions +//---------------------------------------------------------------------------- + /** ** Handle scrolling area. +** +** @param TempScrollState Scroll direction/state. +** @param FastScroll Flag scroll faster. +** +** FIXME: Support dynamic acceleration of scroll speed. */ local void DoScrollArea(enum _scroll_state_ TempScrollState, int FastScroll) { - switch( TempScrollState ) { case ScrollUp: if( MapY ) { @@ -257,7 +272,9 @@ global void UpdateDisplay(void) DrawMissiles(); SetClipping(0,0,VideoWidth,VideoHeight); } + // FIXME: trick17! must find a better solution + // Resources over map! if( TheUI.MapX<TheUI.ResourceX && TheUI.MapWidth>TheUI.ResourceX ) { MustRedraw|=RedrawResources; } @@ -278,7 +295,7 @@ global void UpdateDisplay(void) ,TheUI.MenuButton.Graphic->Width ,TheUI.MenuButton.Graphic->Height ,TheUI.MenuButtonX,TheUI.MenuButtonY); - // FIXME: Button position is configured + DrawMenuButton(MBUTTON_MAIN, (ButtonUnderCursor == 0 ? MenuButtonActive : 0)| (GameMenuButtonClicked ? MenuButtonClicked : 0), @@ -437,20 +454,22 @@ global void GameMainLoop(void) MustRedraw&=~RedrawMinimap; // FIXME: this a little hack! - /* - ** Called each second. Split into different frames. - ** Increment mana of magic units. - ** Update mini-map. - ** Update map fog of war. - ** Call AI. - ** Check game goals. - */ + // + // Work todo each second. + // Split into different frames, to reduce cpu time. + // Increment mana of magic units. + // Update mini-map. + // Update map fog of war. + // Call AI. + // Check game goals. + // Check rescue of units. + // switch( FrameCounter%FRAMES_PER_SECOND ) { case 0: UnitIncrementMana(); // magic units break; case 1: - //UnitIncrementHealth();// berserker healing + UnitIncrementHealth(); // berserker healing break; case 2: MapUpdateVisible(); @@ -467,8 +486,14 @@ global void GameMainLoop(void) case 6: RegenerateForest(); break; + case 7: + RescueUnits(); + break; } } + // + // Map scrolling + // if( TheUI.MouseScroll && !(FrameCounter%SpeedMouseScroll) ) { DoScrollArea(MouseScrollState, 0); } @@ -502,16 +527,9 @@ global void GameMainLoop(void) WaitEventsAndKeepSync(); +#ifndef NEW_NETWORK NetworkSync(); // FIXME: wrong position -#if 0 - // - // Sync: not needed done by DoEvent - // - while( VideoSyncSpeed && VideoInterrupts<1 ) { - sigpause(0); - } #endif - VideoInterrupts=0; } } diff --git a/stratagus/player.cpp b/stratagus/player.cpp index af670ebe7..97b4bb0af 100644 --- a/stratagus/player.cpp +++ b/stratagus/player.cpp @@ -151,6 +151,58 @@ global void CreatePlayer(char* name,int type) player->Enemy=0; player->Allied=0; player->AiNum=PlayerAiUniversal; + + // + // Calculate enemy/allied mask. + // + for( i=0; i<NumPlayers; ++i ) { + switch( type ) { + case PlayerNeutral: + case PlayerNobody: + default: + break; + case PlayerComputer: + // Computer allied with computer and enemy of all humans. + if( Players[i].Type==PlayerComputer ) { + player->Allied|=(1<<i); + Players[i].Allied|=(1<<NumPlayers); + } else if( Players[i].Type==PlayerHuman + || Players[i].Type==PlayerRescueActive ) { + player->Enemy|=(1<<i); + Players[i].Enemy|=(1<<NumPlayers); + } + break; + case PlayerHuman: + // Humans are enemy of all? + if( Players[i].Type==PlayerComputer + || Players[i].Type==PlayerHuman ) { + player->Enemy|=(1<<i); + Players[i].Enemy|=(1<<NumPlayers); + } else if( Players[i].Type==PlayerRescueActive + || Players[i].Type==PlayerRescuePassive ) { + player->Allied|=(1<<i); + Players[i].Allied|=(1<<NumPlayers); + } + break; + case PlayerRescuePassive: + // Rescue passive are allied with humans + if( Players[i].Type==PlayerHuman ) { + player->Allied|=(1<<i); + Players[i].Allied|=(1<<NumPlayers); + } + break; + case PlayerRescueActive: + // Rescue active are allied with humans and enemies of computer + if( Players[i].Type==PlayerComputer ) { + player->Enemy|=(1<<i); + Players[i].Enemy|=(1<<NumPlayers); + } else if( Players[i].Type==PlayerHuman ) { + player->Allied|=(1<<i); + Players[i].Allied|=(1<<NumPlayers); + } + break; + } + } // // Initial default resources. diff --git a/ui/mouse.cpp b/ui/mouse.cpp index 6784d40cf..909dfa15c 100644 --- a/ui/mouse.cpp +++ b/ui/mouse.cpp @@ -288,12 +288,11 @@ global void DoRightButton(int x,int y) // if( action==MouseActionAttack ) { // FIXME: more units on same tile + // FIXME: should use enemy first, than other functions! dest=TargetOnMapTile(unit,x,y); if( dest ) { dest->Blink=3; - if( dest->Player==ThisPlayer || - dest->Player->Player==PlayerNumNeutral) { - // FIXME: lokh: maybe we should add the ally players here + if( dest->Player==ThisPlayer || IsAllied(ThisPlayer,dest) ) { SendCommandFollow(unit,dest,flush); continue; } else { diff --git a/unit/unit.cpp b/unit/unit.cpp index 962db2f39..808931d3e 100644 --- a/unit/unit.cpp +++ b/unit/unit.cpp @@ -328,7 +328,9 @@ global Unit* MakeUnit(UnitType* type,Player* player) if( !type->Building ) { unit->Heading=(MyRand()>>13)&7; // random heading player->NumFoodUnits++; // food needed - MustRedraw|=RedrawResources; // update food + if( player==ThisPlayer ) { + MustRedraw|=RedrawResources;// update food + } } else { player->NumBuildings++; } @@ -529,6 +531,7 @@ global void UnitLost(const Unit* unit) if( unit->Type->Building ) { // FIXME: This should be complete rewritten // FIXME: Slow and new members are available + // FIXME: most redraws only needed for player==ThisPlayer // Still under construction if( unit->Command.Action!=UnitActionBuilded ) { @@ -799,8 +802,57 @@ global void UnitIncrementMana(void) #endif } +/** +** Increment health of all regenerating units. Called each second. +*/ +global void UnitIncrementHealth(void) +{ +#ifdef NEW_UNIT + Unit** table; + Unit* unit; + static UnitType* berserker; + static int regeneration; + + if( !berserker ) { // FIXME: can move to init code! + berserker=UnitTypeByIdent("unit-berserker"); + regeneration=UpgradeIdByIdent("upgrade-berserker-regeneration"); + } + + for( table=Units; table<Units+NumUnits; table++ ) { + unit=*table; + if( unit->Type==berserker + && unit->HP<unit->Stats->HitPoints + && UpgradeIdAllowed(unit->Player,regeneration)=='R' ) { + ++unit->HP; // FIXME: how fast do we regenerate + } + } +#else + Unit* unit; + int i; + static UnitType* berserker; + static int regeneration; + + if( !berserker ) { + berserker=UnitTypeByIdent("unit-berserker"); + regeneration=UpgradeIdByIdent("upgrade-berserker-regeneration"); + } + + for( i=0; i< NumUnits; i++) { + unit=Units[i]; + if( unit->Type==berserker + && unit->HP<unit->Stats->HP + && UpgradeIdAllowed(unit->Player,regeneration)=='R' ) { + ++unit->HP; // FIXME: how fast do we regenerate + } + } +#endif +} + /** ** Change the unit's owner +** +** @param oldplayer Old owning player. +** @param newplayer New owning player. */ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer) { @@ -821,6 +873,11 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer) } } + // + // Must change food/gold and other. + // + UnitLost(unit); + #ifdef NEW_UNIT // Remove from old player table @@ -845,6 +902,110 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer) #endif unit->Player=newplayer; + + // + // Must change food/gold and other. + // + if( unit->Type->GivesOil ) { + DebugLevel0(__FUNCTION__":FIXME: oil platform transfer unsupported\n"); + } + if( !unit->Type->Building ) { + newplayer->NumFoodUnits++; // food needed + if( newplayer==ThisPlayer ) { + MustRedraw|=RedrawResources;// update food + } + } else { + newplayer->NumBuildings++; + } + newplayer->UnitTypesCount[unit->Type->Type]++; + UpdateForNewUnit(unit,0); +} + +/** +** Change the owner of all units of a player. +** +** @param oldplayer Old owning player. +** @param newplayer New owning player. +*/ +local void ChangePlayerOwner(Player* oldplayer,Player* newplayer) +{ + Unit* table[MAX_UNITS]; + Unit* unit; + int i; + int n; + + // NOTE: table is changed. + n=oldplayer->TotalNumUnits; + memcpy(table,oldplayer->Units,n*sizeof(Unit*)); + for( i=0; i<n; i++ ) { + unit=table[i]; + ChangeUnitOwner(unit,oldplayer,newplayer); + } +} + +/** +** Rescue units. +*/ +global void RescueUnits(void) +{ + Player* p; + Unit* unit; + Unit* table[MAX_UNITS]; + Unit* near[MAX_UNITS]; + int n; + int i; + int j; + int l; + static int norescue; + + if( norescue ) { + return; + } + norescue=1; + for( p=Players; p<Players+NumPlayers; ++p ) { + if( p->Type!=PlayerRescuePassive && p->Type!=PlayerRescueActive ) { + continue; + } + if( p->TotalNumUnits ) { + norescue=0; + // NOTE: table is changed. + l=p->TotalNumUnits; + memcpy(table,p->Units,l*sizeof(Unit*)); + for( j=0; j<l; j++ ) { + unit=table[j]; + DebugLevel3("Checking %Zd\n",UnitNumber(unit)); + // NOTE: I hope SelectUnits checks bounds? + n=SelectUnits( + unit->X-1,unit->Y-1, + unit->X+unit->Type->TileWidth+1, + unit->Y+unit->Type->TileHeight+1,near); + // + // Look if human near the unit. + // + for( i=0; i<n; ++i ) { + if( near[i]->Player->Type==PlayerHuman ) { + ChangeUnitOwner(unit,unit->Player,near[i]->Player); + // FIXME: more races? + if( unit->Player->Race==PlayerRaceHuman ) { + PlayGameSound(GameSounds.HumanRescue.Sound + ,MaxSampleVolume); + } else { + PlayGameSound(GameSounds.OrcRescue.Sound + ,MaxSampleVolume); + } + // + // City center converts complete race + // NOTE: I use a trick here, centers could + // store gold. + if( unit->Type->StoresGold ) { + ChangePlayerOwner(p,unit->Player); + } + break; + } + } + } + } + } } /*---------------------------------------------------------------------------- @@ -2366,6 +2527,7 @@ global int ViewPointDistanceToUnit(Unit* dest) { return MapDistanceToUnit(x_v,y_v,dest); } +#if 0 /** ** Check if unit is an enemy. ** @@ -2387,6 +2549,33 @@ global int IsEnemy(const Player* player,const Unit* dest) } return 1; } +#else +/** +** Check if unit is an enemy. +** +** @param player The source player. +** @param dest The destination unit. +** +** @return Returns true, if the destination unit is an enemy. +*/ +global int IsEnemy(const Player* player,const Unit* dest) +{ + return player->Enemy&(1<<dest->Player->Player); +} + +/** +** Check if unit is allied. +** +** @param player The source player. +** @param dest The destination unit. +** +** @return Returns true, if the destination unit is allied. +*/ +global int IsAllied(const Player* player,const Unit* dest) +{ + return player->Allied&(1<<dest->Player->Player); +} +#endif /** ** Can the source unit attack the destionation unit.